﻿/*
	Class: Container
	
		This class handles the <div /> tag and is also the base class for
		all of the visual tags, e.g., <image />, <video /> and <text />.
		
		A large responsibility of this class is to handle the layout of 
		containers. This first involves converting style values for left, top,
		right, bottom, margin, border and padding like "40%," "auto" or
		undefined into actual pixel values.
		
		Another large responsibility is to then layout the containers according
		to their float and clear styles or according to the Frontal-unique
		layout style which may be used to stack containers in top of one
		another.
		
		Frontal owes a lot in the processes to the CSS2 specification but their
		are differences primarily to make things more intuitive in our 
		perspective. For example, when laying out floats, we use a simple set of
		rules that results in very similar layouts as in HTML in most practical
		cases. In complex cases, the layouts will differ but we feel the Frontal
		layout will always be more intuitive.

		Also, we interpert the meaning of right and bottom differently than in
		CSS2 in that for us, the following causes the inner container to fill
		its parent:
		
		<div>
			<div style="left: 0px; right: 100%; top: 0px; bottom: 100%" />
		</div>
		
		whereas in CSS2, right and bottom are distances from the parent's right 
		and bottom edges and so one would specify "right: 0px; bottom: 0px;" 
		This need to subtract to specify the placement of the edge seems less
		intuitive to us than our approach.
*/
package com.frontalcode 
{
	import fl.transitions.*;
	import fl.transitions.easing.*;
	import flash.events.*;
	import flash.display.*;
	import flash.utils.*;
	import flash.geom.*;
	import flash.text.*;
	import flash.external.ExternalInterface;
	
	/**
	 * The Container class handles the <div /> tag and is also a base class 
	 * for all of the visual tags, e.g., <image />, <video /> and <text />.
	 */
    public class Container extends DocumentElement
    {
		public var movie : FrontalSprite;
		
		private var _needsLayout : Boolean = true;
		
		/**
		 * XXX We need to lay ourselves out if a sibling changes its layout 
		 * while we are using leftover dimensions. This could be optimized on
		 * horizontal and vertical dimensions.
		 *
		 * @private
		 */
		internal var usingLeftover : Boolean;
		
		/**
		 * @private
		 */
		internal var needsLayoutThisRender : Boolean;

		private var _mask : DisplayObject;
		
		/*
			Variable: childrenMovies
			
				Keep a Dictionary to map one of our children movies to a 
				Container.
		*/
		/**
		 * @private
		 */
		public var childrenMovies : Dictionary = new Dictionary ( true );
		
		// Track our last dimensions so that we can generate a resize event.
		//
		/**
		 * @private
		 */
		internal var lastWidth : Number;
		
		/**
		 * @private
		 */
		internal var lastHeight : Number;
		
		/**
		 * @private
		 */
		internal var lastScrollbarWidth : Number = 0;
		
		/**
		 * @private
		 */
		internal var lastScrollbarHeight : Number = 0;
		
		/**
		 * @private
		 */
		internal var lastIntrinsicWidth : Number;
		
		/**
		 * @private
		 */
		internal var lastIntrinsicHeight : Number;
		
		/**
		 * @private
		 */
		internal var childNeedsLayoutThisRender : Boolean;
		
		/**
		 * @private
		 */
		private var __explicitContainerWidth : Number;
		
		/**
		 * @private
		 */
		private var __explicitContainerHeight : Number;
		
		private var usingWidthDimensions : Dictionary = new Dictionary ( ); 
		private var usingHeightDimensions : Dictionary = new Dictionary ( ); 
		private var layoutFromResize : Boolean = false;
		private var needsLayoutOnResize : Boolean;
		
		/**
		 * @private
		 */
		internal var resizeContainer : Container;

		// Scrollable container variables.
		//
		
		/**
		 * @private
		 */
		internal var isScrollbar : Boolean = false;						// Is this Container and its children acting as a scrollbar?
		
		/**
		 * @private
		 */
		internal var scrollbarVert : Container;							// If non-null, a container's vertical scrollbar Container. 
		
		/**
		 * @private
		 */
		internal var scrollbarHoriz : Container;							// If non-null, a container's horizontal scrollbar Container. 
		
		/**
		 * @private
		 */
		internal var scrollTopTween : Tween;								// The tween for vertical scrolling.
		
		/**
		 * @private
		 */
		internal var scrollLeftTween : Tween;							// The tween for horizontal scrolling.
		
		/**
		 * @private
		 */
		internal var _scrollTop : Number = 0;							// The current scrollTop value. (Use the getter and setter to access.)
		
		/**
		 * @private
		 */
		internal var _scrollLeft : Number = 0;							// The current scrollLeft value. (Use the getter and setter to access.)
		
		/**
		 * @private
		 */
		internal var scrollbarsState : Object = { };						// The state of our scrollbar, e.g., is a button currently pressed?
		
		/**
		 * @private
		 */
		internal var scrollbarDragging : Container; 						// The scrollbar with a slider being dragged or null.
		
		/**
		 * @private
		 */
		static private var addingScrollbar : Boolean = false;	// To avoid adding scrollbars to scrollbars.
		
		/**
		 * @private
		 */
		private var scrollbarContainer : Container;				// Cache the result of getScrollbarContainer.
		
		// Cache some of our styles and calculations to speed rendering. These
		// should be treated read-only!
		//
		private var childrenPositions : Array = [ ];
		private var childrenDimensions : Rectangle = new Rectangle ( );
		private var siblingsLayoutCalculated : Boolean;
		
		/**
		 * @private
		 */
		public var left : *;
		
		/**
		 * @private
		 */
		public var right : *;
		
		/**
		 * @private
		 */
		public var top : *;
		
		/**
		 * @private
		 */
		public var bottom : *;
		
		/**
		 * @private
		 */
		public var marginTop : *;
		
		/**
		 * @private
		 */
		public var marginBottom : *;
		
		/**
		 * @private
		 */
		public var marginLeft : *;
		
		/**
		 * @private
		 */
		public var marginRight : *;
		
		/**
		 * @private
		 */
		public var paddingTop : *;
		
		/**
		 * @private
		 */
		public var paddingBottom : *;
		
		/**
		 * @private
		 */
		public var paddingLeft : *;
		
		/**
		 * @private
		 */
		public var paddingRight : *;
		
		/**
		 * @private
		 */
		public var borderTop : *;
		
		/**
		 * @private
		 */
		public var borderBottom : *;
		
		/**
		 * @private
		 */
		public var borderLeft : *;
		
		/**
		 * @private
		 */
		public var borderRight : *;
		
		/**
		 * @private
		 */
		public var width : *;
		
		/**
		 * @private
		 */
		public var height : *;
		
		/**
		 * @private
		 */
		internal var scaleX : Number;
		
		/**
		 * @private
		 */
		internal var scaleY : Number;
		
		/**
		 * Creates a new Container instance.
		 * 
		 * @param document	The document this element belongs to.
		 * @param parent	The element's parent in the document tree.
		 * @param node		The XML node this element is associated with.
		 * @param options	Options affecting the behavior of this instance.
		 * 					(This object does not have built-in support for any
		 * 					options.)
		 */
		public function Container ( document : Document, parent : DocumentElement, node : XML, options : Object = null )
		{
			super ( document, parent, node, options );
			
			movie = new FrontalSprite ( this );
			if ( parentContainer != null ) parentContainer.childrenMovies [ movie ] = this;
		}
		
		/**
		 * Destroys this instance.
		 */
		override public function destroy ( ) : void 
		{ 
			super.destroy ( );
			if ( movie != null )
			{
				if ( parentContainer != null ) delete parentContainer.childrenMovies [ movie ];
				if ( movie.parent != null ) movie.parent.removeChild ( movie );
			}
			if ( containingBlock != null ) containingBlock.needsLayout = true;
		}
		
		public function get needsLayout ( ) : Boolean 
		{ 
			return this is Container && ( _needsLayout || attributes.styles.styleCategoryHasChanges ( "layout" ) ); 
		}
		
		public function set needsLayout ( value : Boolean ) : void 
		{ 
			if ( ! ( this is Container ) ) return;
			
			if ( value != _needsLayout )
			{
				if ( debugOptions [ "render" ] ) trace ( node.@id, "needsLayout", value );
				_needsLayout = value;
			}
			
			if ( value ) 
			{
				needsRender = true;
				
				if ( containingBlock != null )
				{
					// If our parent depends on us to get its dimensions then it 
					// also needs a layout. This is not the case though if we
					// are trigger layouts based on our parent's resize events
					// as in that case the parent has already been laid out.
					//
					if ( ! layoutFromResize && ( ! containingBlock.widthSpecified || ! containingBlock.heightSpecified ) )
					{
						containingBlock.needsLayout = true;
					}

					for ( var i : uint = 0; i < containingBlock.containers.length; i++ )
					{
						if ( containingBlock.containers [ i ].usingLeftover ) 
						{
							// Set these via a shortcut so we don't go thru the
							// whole process of dirtying the tree over and over.
							//
							containingBlock.containers [ i ]._needsRender = true;
							containingBlock.containers [ i ]._needsLayout = true;
						}
					}
				}
			}
		}
		
		/**
		 * @private
		 */
		public function get display ( ) : String { return getStyle ( "display" ); }

		/**
		 * @private
		 */
		public function set display ( value : String ) : void { setStyle ( "display", value ); }
		
		/**
		 * Get the position of this element relative to its parent's container.
		 */
		public function get position ( ) : Point
		{
			if ( parent == null || parentContainer.layoutChildren ( ).length <= childIndex ) return new Point ( 0, 0 );
			return parentContainer.layoutChildren ( ) [ childIndex ].topLeft;
		}
		
		/**
		 * Get the X position of this element relative to its parent's 
		 * container.
		 */
		public function get xPos ( ) : Number { return position.x; }
		
		/**
		 * Get the Y position of this element relative to its parent's 
		 * container.
		 */
		public function get yPos ( ) : Number { return position.y; }
		
		// In the following, we convert non-numbers to zero. A value may be a
		// non-number during leftover calculations. That is, something like
		// "left: 100% leftover;" cannot be evaluated until the auto elements
		// have been calculated and so is a non-number until that happens.
		//
		
		/**
		 * Get the total margin width of the element.
		 */
		public function get marginWidth	 ( ) : Number { return ( isNaN ( marginLeft ) ? 0 : marginLeft ) + ( isNaN ( marginRight ) ? 0 : marginRight ); }
		
		/**
		 * Get the total margin height of the element.
		 */
		public function get marginHeight ( ) : Number { return ( isNaN ( marginTop ) ? 0 : marginTop ) + ( isNaN ( marginBottom ) ? 0 : marginBottom ); }
		
		/**
		 * Get the total padding width of the element.
		 */
		public function get paddingWidth  ( ) : Number { return ( isNaN ( paddingLeft ) ? 0 : paddingLeft ) + ( isNaN ( paddingRight ) ? 0 : paddingRight ); }
		
		/**
		 * Get the total padding height of the element.
		 */
		public function get paddingHeight ( ) : Number { return ( isNaN ( paddingTop ) ? 0 : paddingTop ) + ( isNaN ( paddingBottom ) ? 0 : paddingBottom ); }
		
		/**
		 * Get the total border width of the element.
		 */
		public function get borderWidth  ( ) : Number { return ( isNaN ( borderLeft ) ? 0 : borderLeft ) + ( isNaN ( borderRight ) ? 0 : borderRight ); }
		
		/**
		 * Get the total border height of the element.
		 */
		public function get borderHeight ( ) : Number { return ( isNaN ( borderTop ) ? 0 : borderTop ) + ( isNaN ( borderBottom ) ? 0 : borderBottom ); }
		
		/*
			We return null if the overflow style is "visible." If the style is
			set to "hidden" then "mask" is the class name of the movieclip that
			will act as the mask and we return the class reference. If the
			"mask" style isn't set then we return undefined.
		*/
		/**
		 * @private
		 */
		public function get maskClass ( ) : *
		{ 
			if ( getStyle ( "overflow" ) == "visible" ) return null;
			var name : * = getStyle ( "mask" );
			if ( name != null ) return document.externalAssetManager.getDefinition ( name, checkMaskClassOnLoadExternalAsset );
			return name;
		}
		
		/*
			Function: checkMaskClassOnLoadExternalAsset
			
				This method is called when an external asset has been loaded and 
				resolves our mask symbol.
		*/
		/**
		 * @private
		 */
		internal function checkMaskClassOnLoadExternalAsset ( name : String, externalAssetSrc : String, classRef : Class ) : void
		{
			needsRender = true;
		}
		
		// The containing block (a CSS term) is the container that defines our
		// coordinate space. This is our parent container except when we have
		// resize anchors or a resize target. 
		//
		/**
		 * @private
		 */
		public function get containingBlock ( ) : Container
		{
			if ( attributes != null )
			{
				// If both pin-to-container and pin-to-stage are set then the
				// former takes precedence.
				//
				if ( getStyle ( "pin-to-container" ) != null ) return getContainerById ( getStyle ( "pin-to-container" ) );
				if ( getStyle ( "pin-to-stage" ) ) return null;
			}
			
			return parentContainer;
		}
		
		/*
			Function: contentDimensions
			
				contentDimensions returns the width and height of the content
				area of our Container. That is, this is the area of our
				container that excludes margins, padding, borders and
				scrollbars. If our width and height are set via styles then
				these are the limits. Otherwise, the limit is taken from the
				maximum width and height of our children containers.
				
				Note, in these calculations the container's scale is NOT applied
				to the result. This is because we expect these dimensions to be
				of interest to the children containers which are working inside
				the unscaled coordinate space.
		*/
		/**
		 * @private
		 */
		public function contentDimensions ( allowOverFlow : Boolean = false ) : Rectangle
		{
			// Note that we only consider the positive size of our children
			// containers.
			//
			var result : Rectangle = new Rectangle ( );
			
			if ( childrenPositionsSet )
			{
				result.width  = Math.max ( 0, childrenDimensions.right );
				result.height = Math.max ( 0, childrenDimensions.bottom );
			}
			
			// Now consider our intrinsic width and height.
			//
			if ( ! isNaN ( intrinsicWidth  ) ) result.width  = Math.max ( intrinsicWidth,  result.width );
			if ( ! isNaN ( intrinsicHeight ) ) result.height = Math.max ( intrinsicHeight, result.height );
			
			// If we have specified a width or a height via a style then this
			// sets our dimensions if the allowOverFlow is not set or if these
			// dimensions are larger than what we've calculated so far.
			//
			if ( widthSpecified  && ! isNaN ( width  ) && ( ! allowOverFlow || width  > result.width  ) ) result.width  = width;
			if ( heightSpecified && ! isNaN ( height ) && ( ! allowOverFlow || height > result.height ) ) result.height = height;

			if ( ! allowOverFlow )
			{			
				// If we're not allowing overflow then remove the space used by 
				// our scroll bars. Otherwise we ignore the fact that there may
				// be scrollbars.
				//
				if ( result.width  > scrollbarWidth  ) result.width  -= scrollbarWidth;
				if ( result.height > scrollbarHeight ) result.height -= scrollbarHeight;
			}
			
			return result;
		}
		
		/**
		 * Get the width of the content area of the element.
		 */
		public function get contentWidth  ( ) : Number { return contentDimensions ( ).width;  }
		
		/**
		 * Get the height of the content area of the element.
		 */
		public function get contentHeight ( ) : Number { return contentDimensions ( ).height; }
		
		/**
		 * Get the width of the content within the element.
		 */
		public function get unboundedContentWidth  ( ) : Number { return contentDimensions ( true ).width;  }
		
		/**
		 * Get the height of the content within the element.
		 */
		public function get unboundedContentHeight ( ) : Number { return contentDimensions ( true ).height; }
		
		// containerWidth and containerHeight are the dimensions of our 
		// container as viewed in our parent's coordinate space. This means that
		// scale and rotation affect our dimensions. Also note that our width
		// and height cannot be negative. If our container is totally in the
		// negative x coordinate space then its as if our containerWidth were
		// zero. (This is by virtue of our calculation of contentWidth.) A 
		// similar statement can be made for containerHeight. It's unclear to me 
		// if this is a limitation.
		//
		// XXX We do not account for rotation here. Perhaps we should though we
		// would have to assume the container were rectangular.
		//
		
		/**
		 * Get the width of the element as viewed in our parent's coordinate 
		 * space.
		 */
		public function get containerWidth ( ) : Number
		{
			if ( display == "none" ) return 0;
			return scaleX * ( contentWidth + paddingWidth + borderWidth + marginWidth + scrollbarWidth );
		}
		
		/**
		 * Get the height of the element as viewed in our parent's coordinate 
		 * space.
		 */
		public function get containerHeight ( ) : Number
		{
			if ( display == "none" ) return 0;
			return scaleY * ( contentHeight + paddingHeight + borderHeight + marginHeight + scrollbarHeight );
		}
		
		/*
			Properties: explicitContainerWidth, explicitContainerHeight
			
				explicitContainerWidth and explicitContainerHeight are the
				dimensions of our container determined by our styles. This is
				needed for example when we containing floating containers that
				want to know where to wrap. If we do not have the explicit
				dimension then we ask our parent for its explicit dimensions.
				This may continue up to the document which will return the
				Stage's dimensions. 
				
				Note, in this case the container's scale is NOT applied to the
				result. This is because we expect the explicit dimensions to be
				of interest to the children containers which are working inside
				the unscaled coordinate space.
				
				Also along those lines, these dimensions do NOT include the
				space used by scrollbars. This is again because these dimensions
				are targeted at the children containers which need to know the
				available space which does not include scrollbars.
		*/
		/**
		 * @private
		 */
		internal function get containingBlockExplicitWidth ( ) : Number
		{ 
			return containingBlock != null ? containingBlock._explicitContainerWidth : document.containingBlockExplicitWidth; 
		}
		
		/**
		 * @private
		 */
		internal function get _explicitContainerWidth ( ) : Number
		{
			var value : * = width;
			if ( isNaN ( value ) ) 
			{
				if ( containingBlock != null )
				{
					addResizeListener ( containingBlock );					
				}
				else
				{
					addStageResizeListener ( );
				}
				value = containingBlockExplicitWidth / scaleX;
				var fixed : Number = paddingWidth + borderWidth + marginWidth;
				if ( ! isNaN ( left  ) ) fixed += left;
				if ( ! isNaN ( right ) ) fixed += value - right;
				value -= fixed;
			}
			value -= scrollbarWidth;
			return Number ( value );
		}
		
		/**
		 * @private
		 */
		internal function cacheExplicitContainerWidth ( force : Boolean = false ) : void
		{
			// When reporting the explicitContainerWidth we always use the 
			// cached value. Setting this value to NaN is our way of 
			// invalidating the cache.
			//
			if ( force || isNaN ( __explicitContainerWidth ) ) 
			{
				__explicitContainerWidth = _explicitContainerWidth;
				if ( debugOptions [ "render" ] ) trace ( node.@id, "cacheExplicitContainerWidth", __explicitContainerWidth );
			}
		}

		/**
		 * @private
		 */
		public function get explicitContainerWidth ( ) : Number
		{
			if ( display == "none" || isNaN ( __explicitContainerWidth ) ) return 0;
			return __explicitContainerWidth;
		}
		
		/**
		 * @private
		 */
		internal function get containingBlockExplicitHeight ( ) : Number 
		{ 
			return containingBlock != null ? containingBlock._explicitContainerHeight : document.containingBlockExplicitHeight; 
		}
		
		/**
		 * @private
		 */
		internal function get _explicitContainerHeight ( ) : Number
		{
			var value : * = height;
			if ( isNaN ( value ) ) 
			{
				if ( containingBlock != null )
				{
					addResizeListener ( containingBlock );					
				}
				else
				{
					addStageResizeListener ( );
				}
				value = containingBlockExplicitHeight / scaleY;
				var fixed : Number = paddingHeight + borderHeight + marginHeight;
				if ( ! isNaN ( top    ) ) fixed += top;
				if ( ! isNaN ( bottom ) ) fixed += value - bottom;
				value -= fixed;
			}
			value -= scrollbarHeight;
			return Number ( value );
		}
		
		/**
		 * @private
		 */
		internal function cacheExplicitContainerHeight ( force : Boolean = false ) : void
		{
			// When reporting the explicitContainerHeight we always use the 
			// cached value. Setting this value to NaN is our way of 
			// invalidating the cache.
			//
			if ( force || isNaN ( __explicitContainerHeight ) ) 
			{
				__explicitContainerHeight = _explicitContainerHeight;
				if ( debugOptions [ "render" ] ) trace ( node.@id, "cacheExplicitContainerHeight", __explicitContainerHeight );
			}
		}
		
		/**
		 * @private
		 */
		public function get explicitContainerHeight ( ) : Number
		{
			if ( display == "none" || isNaN ( __explicitContainerHeight ) ) return 0;
			return __explicitContainerHeight;
		}
		
		/**
		 * Get the intrinsic width of this element.
		 */
		public function get intrinsicWidth ( ) : Number { return Number.NaN; }
		
		/**
		 * Get the intrinsic height of this element.
		 */
		public function get intrinsicHeight ( ) : Number { return Number.NaN; }
		
		/**
		 * @private
		 */
		internal function addStageResizeListener    ( ) : void { addListener    ( document.movieParent.stage, Event.RESIZE, stageResizeHandler ); }

		/**
		 * @private
		 */
		internal function removeStageResizeListener ( ) : void { removeListener ( document.movieParent.stage, Event.RESIZE, stageResizeHandler ); }
		
		/**
		 * @private
		 */
		internal function stageResizeHandler ( event : Event ) : void
		{
			if ( needsLayoutOnResize && ( event.target as Stage ).fullScreenSourceRect == null )
			{
				needsLayout = true;
			}
		}
		
		/**
		 * @private
		 */
		internal function addResizeListener ( container : Container ) : void
		{
			addListener ( container, FrontalEvent.RESIZE, resizeHandler );
			resizeContainer = container;
		}
		
		/**
		 * @private
		 */
		internal function removeResizeListener ( ) : void 
		{
			if ( resizeContainer != null )
			{
				removeListener ( resizeContainer, FrontalEvent.RESIZE, resizeHandler );
				resizeContainer = null
			}
		}
		
		/**
		 * @private
		 */
		internal function resizeHandler ( event : Event ) : void 
		{ 
			if ( needsLayoutOnResize ) 
			{
				var resized : Container = event.target as Container;
				
				// Our containing block resized but does it really have new 
				// dimensions? And if it does, are we using them?
				//
				if ( 
						( ( event as FrontalEvent ).details != null && ( event as FrontalEvent ).details.force ) ||
						( usingWidthDimensions [ resized ] != undefined && usingWidthDimensions [ resized ] != resized.contentWidth ) || 
						( usingHeightDimensions [ resized ] != undefined && usingHeightDimensions [ resized ] != resized.contentHeight )
					)
				{
					if ( debugOptions [ "render" ] ) trace ( node.@id, "resizeHandler", "(", usingWidthDimensions [ resized ], ",", usingHeightDimensions [ resized ], ") -> (", resized.contentWidth, ",", resized.contentHeight, ")" );
					
					// Clear our explicit dimensions so that they get 
					// recalculated.
					//
					__explicitContainerHeight = Number.NaN;
					__explicitContainerWidth = Number.NaN;
					
					// Since this layout is from a resize of our containing 
					// block, it doesn't need to be laid out. That's what this
					// flag is indicating.
					//
					layoutFromResize = true;
					needsLayout = true;
					layoutFromResize = false;
					dispatchEvent ( new FrontalEvent ( FrontalEvent.RESIZE, { force: true } ) );
				}
			}
			else
			{
				dispatchEvent ( new FrontalEvent ( FrontalEvent.RESIZE ) );
			}
		}
		
		/**
		 * @private
		 */
		override public function initializeRender ( ) : void
		{
			if ( debugOptions [ "render" ] ) trace ( node.@id, "initializeRender" );
			
			super.initializeRender ( );
			
			needsLayoutThisRender = needsLayout;
			needsLayout = false; 

			if ( needsLayoutThisRender || ( parentContainer != null && parentContainer.childNeedsLayoutThisRender ) )
			{
				if ( debugOptions [ "render" ] ) trace ( node.@id, "clearingRenderInfo" );
			
				clearLayoutCache ( );
				usingLeftover = false;
				needsLayoutOnResize = false;				
				removeStageResizeListener ( );
				removeResizeListener ( );
			}
		}
		
		// Don't do anything graphical if display is set to none.
		//
		/**
		 * @private
		 */
		override public function renderSelf ( ) : void
		{
			pB ( "Container.renderSelf" );
			
			if ( debugOptions [ "render" ] ) trace ( node.@id, "renderSelf" );
			
			if ( display != "none" ) showChildren ( );
			
			// This will call applyInteractions ( ) and applyStyles ( ).
			//
			super.renderSelf ( );
	
			if ( display != "none" ) 
			{
				customRender ( );
				renderBackground ( );
				renderMask ( );
				renderProgressIndicator ( );
				renderScrollbars ( );
				
				// If our intrinsic dimensions changed and our dimensions depend on 
				// them then we need to do a layout so skip the resize check for 
				// now.
				//
				// We need to do this check at least because of the case that an
				// element with intrinsic dimensions hasn't determined those 
				// dimensions prior to its element being laid out. In that case, the
				// width and height styles of "auto" are calculated as if the 
				// element doesn't have intrinsic dimensions. So when we do find
				// those dimensions we need to do a layout.
				//
				var resizedIntrinsicDimensions : Boolean = false;
				if ( ! widthSpecified  && ! isNaN ( intrinsicWidth  ) && ( isNaN ( lastIntrinsicWidth  ) || Math.abs ( lastIntrinsicWidth  - intrinsicWidth  ) > 0.0001 ) ) resizedIntrinsicDimensions = true;
				if ( ! heightSpecified && ! isNaN ( intrinsicHeight ) && ( isNaN ( lastIntrinsicHeight ) || Math.abs ( lastIntrinsicHeight - intrinsicHeight ) > 0.0001 ) ) resizedIntrinsicDimensions = true;
				
				if ( resizedIntrinsicDimensions )
				{
					if ( debugOptions [ "render" ] ) trace ( node.@id, "resizedIntrinsicDimensions",  "(", lastIntrinsicWidth, ",", lastIntrinsicHeight, ") -> (", intrinsicWidth, ",", intrinsicHeight, ")" );
			
					lastIntrinsicWidth = intrinsicWidth;
					lastIntrinsicHeight = intrinsicHeight;
					needsLayout = true;
				}
			}
			
			pE ( "Container.renderSelf" );
		}
		
		/**
		 * During the rendering process and an may perform custom rendering by
		 * overriding this method. (Be sure to call the base calss method though
		 * as it supports the onCustomRender interaction.)
		 */
		public function customRender ( ) : void { runInteractions ( "onCustomRender" ); }
		
		override public function finalizeRender ( ) : void
		{		
			if ( debugOptions [ "render" ] ) 
			{
				trace ( node.@id, "finalizeRender", "marginLeft:", marginLeft, "borderLeft:", borderLeft, "paddingLeft:", paddingLeft, "contentWidth:", contentWidth, "paddingRight:", paddingRight, "borderRight:", borderRight, "marginRight:", marginRight );
				trace ( node.@id, "finalizeRender", "marginTop:", marginTop, "borderTop:", borderTop, "paddingTop:", paddingTop, "contentHeight:", contentHeight, "paddingBottom:", paddingBottom, "borderBottom:", borderBottom, "marginBottom:", marginBottom );
			}
			
			super.finalizeRender ( );
		}
				
		/**
		 * @private
		 */
		override public function show ( force : * = null ) : void
		{
			// Turning on disable-display-list-mgmt can be very handy when you
			// want to create DisplayObjects in Frontal for use by other Flash
			// movies. For example, when you register a DisplayObject as an icon 
			// in Google Maps, that Flash library attaches that DisplayObject to
			// its own display list. We'd still like Frontal rendering to handle
			// roll overs and such, we just don't want the DisplayObject re-
			// attached to the Frontal display list or removed from the Google
			// maps display list. So we add this style that says don't mess with
			// the whatever display list this element's movie is currently in.
			// 
			if ( movie == null || getStyle ( "disable-display-list-mgmt" ) ) return;
			
			// See if we're attached to the right parent first.
			//
			var movieParent : DisplayObjectContainer = parentAttacher;
			var movieParentContainer : Container = parentContainer;
			
			if ( containingBlock != parentContainer && getStyle ( "pin-movie" ) ) 
			{
				if ( getStyle ( "pin-to-stage" ) )
				{
					movieParent = document.movieParent.stage;
					movieParentContainer = null;
				}
				else
				{
					movieParent = containingBlock != null ? containingBlock.movie : null;
					movieParentContainer = containingBlock;
				}
			}
			
			if ( movie.parent != movieParent )
			{
				if ( movie.parent != null ) movie.parent.removeChild ( movie );
				if ( movieParentContainer != null ) movieParentContainer.childrenMovies [ movie ] = this;
			}
			
			// Now manage our movie in the display list.
			//
			if ( force === true || ( force === null && getStyle ( "display" ) != "none" && getStyle ( "visibility" ) != "hidden"  ) )
			{
				if ( movie.parent == null && movieParent != null ) 
				{
					movieParent.addChild ( movie );
					if ( containingBlock != parentContainer ) repositionPinnedContainer ( );
					resetDepths ( );
				}
			}
			else
			{
				if ( movie.parent != null ) movie.parent.removeChild ( movie );
			}
			
			if ( force !== null ) 
			{
				// Do not re-render because of this style change.
				// XXX Should we be setting display as well?
				//
				
				attributes.styles.setStyle ( "visibility", force ? "visible" : "hidden", false, true );
			}
		}
		
		/**
		 * @private
		 */
		internal function get parentAttacher ( ) : Sprite { return parentContainer.movie; }
		
		/*
			Function: resetDepths
			
				Set our children's movies at the proper depths. Movies that
				don't have associated containers (like those added by
				ContainerImage, ContainerText and ContainerVideo) are placed at
				the lowest depths. The rest are first ordered on their z-index
				and then their childIndex.
		*/
		/**
		 * @private
		 */
		internal function resetDepths ( ) : void
		{
			if ( parent == null || parentAttacher == null ) return;
			
			var indexes : Array = [ ];
			for ( var i : uint = 0; i < parentAttacher.numChildren; i++ )
			{
				var entry : Object = { index: i };
				var movie : DisplayObject = parentAttacher.getChildAt ( i );
				var container : Container = parentContainer.childrenMovies [ movie ];
				entry.movie = movie;
				entry.childIndex = container != null ? container.childIndex : -1;
				entry.zIndex = container == null ? Number.NEGATIVE_INFINITY : container.getStyle ( "z-index" );
				indexes [ i ] = entry;
			}
			indexes.sortOn ( [ "zIndex", "childIndex", "index" ], [ Array.NUMERIC, Array.NUMERIC, Array.NUMERIC ] );
			
			for ( i = 0; i < indexes.length; i++ ) parentAttacher.setChildIndex ( indexes [ i ].movie, i );
		}
		
		internal var lastBackgroundWidth  : Number;
		internal var lastBackgroundHeight : Number;
		internal var lastBackgroundLeft : Number;
		internal var lastBackgroundTop  : Number;
			
		/**
		 * @private
		 */
		public function renderBackground ( ) : void
		{
			// Give the user a chance to render the background. If false is
			// returned then the standard drawing steps will be skipped.
			//
			if ( runInteractions ( "onRenderBackground", { state: "beforeRender" } ) === false ) return;
			
			var bgNeedsRender : Boolean = false;
			var backgroundWidth  : Number = contentWidth  + paddingWidth  + scrollbarWidth  + borderWidth;
			var backgroundHeight : Number = contentHeight + paddingHeight + scrollbarHeight + borderHeight;
			var backgroundLeft : Number = -borderLeft - paddingLeft;
			var backgroundTop  : Number = -borderTop  - paddingTop;

			if ( lastBackgroundWidth != backgroundWidth || lastBackgroundHeight != backgroundHeight || lastBackgroundLeft != backgroundLeft || lastBackgroundTop != backgroundTop || attributes.styles.styleCategoryHasChanges ( "backgroundFormat" ) )
			{
				if ( debugOptions [ "render" ] ) trace ( node.@id, "bgNeedsRender",  "(", lastBackgroundLeft, ",", lastBackgroundTop, ",", lastBackgroundWidth, ",", lastBackgroundHeight, ") -> (", backgroundLeft, ",", backgroundTop, ",", backgroundWidth, ",", backgroundHeight, ")" );
			
				bgNeedsRender = true;
				attributes.styles.clearChanges ( "backgroundFormat" );

				lastBackgroundWidth = backgroundWidth;
				lastBackgroundHeight = backgroundHeight;
				lastBackgroundLeft = backgroundLeft;
				lastBackgroundTop = backgroundTop;

				movie.graphics.clear ( );
				
				if ( getStyle ( "background-color" ) != null || ( getStyle ( "bg-gradient-type" ) != null && getStyle ( "bg-gradient-type" ) != "none" ) )
				{
					if ( getStyle ( "bg-gradient-type" ) != null && getStyle ( "bg-gradient-type" ) != "none" )
					{
						applyGradientStyles ( "beginGradientFill", "bg" );
					}
					else
					{
						movie.graphics.beginFill ( getStyle ( "background-color" ), getStyle ( "background-alpha" ) );
					}
					switch ( getStyle ( "background-shape" ) )
					{
						case "rectangle":
							movie.graphics.drawRect ( backgroundLeft + borderLeft, backgroundTop + borderTop, backgroundWidth - borderWidth, backgroundHeight - borderHeight );
							break;
						case "ellipse":
							movie.graphics.drawEllipse ( backgroundLeft + borderLeft, backgroundTop + borderTop, backgroundWidth - borderWidth, backgroundHeight - borderHeight );
							break;
						case "rounded rectangle":
							var ellipseWidth : Number = evaluateDimensionStyle ( getStyle ( "bg-shape-ellipse-width" ), true );
							var ellipseHeight : Number = evaluateDimensionStyle ( getStyle ( "bg-shape-ellipse-height" ), false );
							movie.graphics.drawRoundRect ( backgroundLeft + borderLeft, backgroundTop + borderTop, backgroundWidth - borderWidth, backgroundHeight - borderHeight, ellipseWidth, ellipseHeight );
							break;
					}
					movie.graphics.endFill ( );
				}
	
				// Check for the special case that the border is the same shape
				// as the background and the sides all have the same width.
				//
				if ( borderTop > 0 && borderTop == borderRight && borderRight == borderLeft && borderLeft == borderBottom )
				{
					movie.graphics.lineStyle ( borderTop, getStyle ( "border-color" ), getStyle ( "border-alpha" ), true, LineScaleMode.NORMAL, CapsStyle.SQUARE, JointStyle.MITER );
					if ( getStyle ( "border-gradient-type" ) != null && getStyle ( "border-gradient-type" ) != "none" ) applyGradientStyles ( "lineGradientStyle", "border" );
					switch ( getStyle ( "border-shape" ) == null ? getStyle ( "background-shape" ) : getStyle ( "border-shape" ) )
					{
						case "rectangle":
							movie.graphics.drawRect ( backgroundLeft + borderLeft / 2, backgroundTop + borderTop / 2, backgroundWidth - borderWidth / 2, backgroundHeight - borderHeight / 2 );
							break;
						case "ellipse":
							movie.graphics.drawEllipse ( backgroundLeft + borderLeft / 2, backgroundTop + borderTop / 2, backgroundWidth - borderWidth / 2, backgroundHeight - borderHeight / 2 );
							break;
						case "rounded rectangle":
							ellipseWidth = evaluateDimensionStyle ( getStyle ( "border-shape-ellipse-width" ) == null ? getStyle ( "bg-shape-ellipse-width" ) : getStyle ( "border-shape-ellipse-width" ), true );
							ellipseHeight = evaluateDimensionStyle ( getStyle ( "border-shape-ellipse-height" ) == null ? getStyle ( "bg-shape-ellipse-height" ) : getStyle ( "border-shape-ellipse-height" ), false );
							movie.graphics.drawRoundRect ( backgroundLeft + borderLeft / 2, backgroundTop + borderTop / 2, backgroundWidth - borderWidth / 2, backgroundHeight - borderHeight / 2, ellipseWidth, ellipseHeight );
							break;
					}
				}
				else
				{
					if ( getStyle ( "border-shape" ) != null && getStyle ( "border-shape" ) != "rectangle" ) Debugger.logMessage ( Debugger.WARNING, "Container", "a \"border-shape\" style other than \"rectangle\" is only supported when all border sides are the same width" ); 
					
					if ( borderTop > 0 )
					{
						movie.graphics.lineStyle ( borderTop, getStyle ( "border-color" ), getStyle ( "border-alpha" ), false, LineScaleMode.NORMAL, CapsStyle.NONE );
						movie.graphics.moveTo ( backgroundLeft, backgroundTop + borderTop / 2 );
						movie.graphics.lineTo ( backgroundLeft + backgroundWidth, backgroundTop + borderTop / 2 );
					}
		
					if ( borderLeft > 0 )
					{
						movie.graphics.lineStyle ( borderLeft, getStyle ( "border-color" ), getStyle ( "border-alpha" ), false, LineScaleMode.NORMAL, CapsStyle.NONE );
						movie.graphics.moveTo ( backgroundLeft + borderLeft / 2, backgroundTop );
						movie.graphics.lineTo ( backgroundLeft + borderLeft / 2, backgroundTop + backgroundHeight );
					}
		
					if ( borderBottom > 0 )
					{
						movie.graphics.lineStyle ( borderBottom, getStyle ( "border-color" ), getStyle ( "border-alpha" ), false, LineScaleMode.NORMAL, CapsStyle.NONE );
						movie.graphics.moveTo ( backgroundLeft, backgroundTop + backgroundHeight - borderBottom / 2 );
						movie.graphics.lineTo ( backgroundLeft + backgroundWidth, backgroundTop + backgroundHeight - borderBottom / 2 );
					}
		
					if ( borderRight > 0 )
					{
						movie.graphics.lineStyle ( borderRight, getStyle ( "border-color" ), getStyle ( "border-alpha" ), false, LineScaleMode.NORMAL, CapsStyle.NONE );
						movie.graphics.moveTo ( backgroundLeft + backgroundWidth - borderRight / 2, backgroundTop );
						movie.graphics.lineTo ( backgroundLeft + backgroundWidth - borderRight / 2, backgroundTop + backgroundHeight );
					}
				}
			}
			
			// Give the user a chance to render on top of the standard 
			// background.
			//
			runInteractions ( "onRenderBackground", { state: "afterRender", bgNeedsRender: bgNeedsRender, bgRect: new Rectangle ( backgroundLeft, backgroundTop, backgroundWidth, backgroundHeight ) } );
		}

		/**
		 * @private
		 */
		internal function applyGradientStyles ( method : String, stylePrefix : String ) : void
		{
			var gradientBoxMatrix : Matrix = null;
			var values : Array = getStyle ( stylePrefix + "-gradient-matrix" );
			if ( values != null )
			{
				gradientBoxMatrix = new Matrix ( );
				
				if ( values.length == 5 )
				{
					// XXX Need to support percentages.
					//
					gradientBoxMatrix.createGradientBox.apply ( gradientBoxMatrix, values ); 
				}
				else // Assert that values.length == 6
				{
					gradientBoxMatrix.a  = values [ 0 ];
					gradientBoxMatrix.b  = values [ 1 ];
					gradientBoxMatrix.c  = values [ 2 ];
					gradientBoxMatrix.d  = values [ 3 ];
					gradientBoxMatrix.tx = values [ 4 ];
					gradientBoxMatrix.ty = values [ 5 ];
				}
			}
			movie.graphics [ method ] ( getStyle ( stylePrefix + "-gradient-type" ), getStyle ( stylePrefix + "-gradient-colors" ), getStyle ( stylePrefix + "-gradient-alphas" ), getStyle ( stylePrefix + "-gradient-ratios" ), gradientBoxMatrix, getStyle ( stylePrefix + "-gradient-spread" ), getStyle ( stylePrefix + "-gradient-interpolation" ), getStyle ( stylePrefix + "-gradient-focal-point-ratio" ) );
		}
		
		/**
		 * @private
		 */
		public function set mask ( value : DisplayObject ) : void
		{
			if ( value != _mask )
			{
				if ( _mask != null )
				{
					movie.mask = null;
					if ( _mask.parent != null ) _mask.parent.removeChild ( _mask );
				}
				
				if ( value != null )
				{
					movie.addChild ( value );
					movie.mask = value;
				}
				
				_mask = value;
			}
		}
		
		/**
		 * @private
		 */
		public function get mask ( ) : DisplayObject { return _mask; }
		
		/**
		 * @private
		 */
		internal var lastMaskWidth : Number;
		
		/**
		 * @private
		 */
		internal var lastMaskHeight : Number;
		
		/**
		 * @private
		 */
		public function renderMask ( ) : void
		{
			/*
				This check isn't enough and it doesn't save any cycles so don't
				do it.
				
				if ( ! attributes.styles.styleCategoryHasChanges ( "maskFormat" ) ) return;
			*/
			attributes.styles.clearChanges ( "maskFormat" );

			if ( getStyle ( "overflow" ) != "hidden" ) 
			{
				mask = null;
				return;
			}
			
			var maskWidth : Number = containerWidth / scaleX - marginWidth;
			var maskHeight : Number = containerHeight / scaleY - marginHeight;
			
			if ( mask != null && lastMaskWidth == maskWidth && lastMaskHeight == maskHeight ) return;

			if ( debugOptions [ "render" ] ) trace ( node.@id, "maskNeedsRender",  "(", lastMaskWidth, ",", lastMaskHeight, ") -> (", maskWidth, ",", maskHeight, ")" );
			
			var maskClassValue : Class = maskClass;
			if ( maskClassValue != null )
			{
				if ( mask == null || ! ( mask is maskClassValue ) )
				{
					// XXX We wnat to know if the classes are exactly the same;
					// this just checks that mask is a subclass of the mask's
					// class.
					//
					mask = new maskClassValue ( );
				}
				
				if ( getStyle ( 'scale-mask' ) )
				{
					// XXX We would like to support scale9Grid for the mask but
					// Flash disables this feature when a MovieClip is used as a
					// mask. At least that was the case for AS2. What about AS3?
					//
					mask.width  = maskWidth;
					mask.height = maskHeight;
				}
			}
			else
			{
				var maskShape : Shape = mask == null ? new Shape ( ) : mask as Shape;

				// Give the user a chance to render the mask. If false is
				// returned then the standard drawing steps will be skipped.
				//
				if ( runInteractions ( "onRenderMask", { state: "beforeRender" } ) !== false )
				{
					maskShape.graphics.clear ( );
					maskShape.graphics.beginFill ( 0 );
					
					switch ( getStyle ( "background-shape" ) )
					{
						case "rectangle":
							maskShape.graphics.drawRect ( 0, 0, maskWidth, maskHeight );
							break;
						case "ellipse":
							maskShape.graphics.drawEllipse ( 0, 0, maskWidth, maskHeight );
							break;
						case "rounded rectangle":
							var ellipseWidth : Number = evaluateDimensionStyle ( getStyle ( "bg-shape-ellipse-width" ), true );
							var ellipseHeight : Number = evaluateDimensionStyle ( getStyle ( "bg-shape-ellipse-height" ), false );
							maskShape.graphics.drawRoundRect ( 0, 0, maskWidth, maskHeight, ellipseWidth, ellipseHeight );
							break;
					}
					
					maskShape.graphics.endFill ( );
			
					// Give the user a chance to render on top of the standard 
					// mask.
					//
					runInteractions ( "onRenderMask", { state: "afterRender", maskRect: new Rectangle ( 0, 0, maskWidth, maskHeight ) } );
				}
				
				mask = maskShape;
			}
			
			lastMaskWidth = maskWidth;
			lastMaskHeight = maskHeight;
		}
		
		/*
			Function: positionMovie
	
				Position a movie such that its border's outer edge is at x, y.
		*/
		/**
		 * @private
		 */
		public function positionMovie ( x : Number = NaN, y : Number = NaN ) : void
		{
			if ( getStyle ( "disable-rendering" ) || getStyle ( "disable-positioning" ) ) return;
			if ( movie == null ) return;

			if ( isNaN ( x ) ) x = xPos;
			if ( isNaN ( y ) ) y = yPos;
			
			var lastX : Number = movie.x;
			var lastY : Number = movie.y;
			
			movie.x = x + movieOffsetX;
			movie.y = y + movieOffsetY;

			if ( ! isScrollbar && containingBlock != null && gS ( "scrollable" ) !== false )
			{
				movie.x -= containingBlock.scrollLeft;
				movie.y -= containingBlock.scrollTop;
			}
			
			if ( mask != null )
			{
				mask.x = - borderLeft - paddingLeft;
				mask.y = - borderTop  - paddingTop;
			}
			
			if ( lastX != movie.x || lastY != movie.y )
			{
				if ( debugOptions [ "render" ] ) 
				{
					trace ( node.@id, "positionMovie",  "local: (", movie.x, ",", movie.y, "), global: ", movie.parent == null ? "--" : movie.parent.localToGlobal ( new Point ( movie.x, movie.y ) ) );
				}
			
				dispatchEvent ( new FrontalEvent ( FrontalEvent.REPOSITION ) );
			}
		}
		
		/*
			Variables: movieOffsetX, movieOffsetY
			
				The container's movie is offset by the width and height of the
				margin, border and padding.
		*/
		/**
		 * @private
		 */
		public function get movieOffsetX ( ) : Number { return scaleX * ( marginLeft + borderLeft + paddingLeft ); }

		/**
		 * @private
		 */
		public function get movieOffsetY ( ) : Number { return scaleY * ( marginTop  + borderTop  + paddingTop ); }
	
		/**
		 * Renders the elements.
		 */
		override public function render ( ) : void 
		{ 
			if ( ! needsRender ) return;
			parent.renderChildren ( );
		}
		
		/*
			Function: renderChildren
	
				Renders child containers. This is a rather complex and iterative
				process described inline below.
				
			Notes:
				
				We do not call DocumentElement's renderChildren.
		*/
		/**
		 * @private
		 */
		override public function renderChildren ( ) : void
		{
			if ( display == "none" ) return;
			
			pB ( "Container.renderChildren" );
			
			var needRender : Array = [ ];
			childNeedsLayoutThisRender = false;
			for ( var i : uint = 0; i < elements.length; i++ ) 
			{
				if ( elements [ i ] is Container )
				{
					// Store those elements that need rendering. This is always
					// a superset of those needing layout so we don't keep 
					// separate arrays.
					//
					if ( elements [ i ].needsRender ) needRender.push ( elements [ i ] );
					if ( elements [ i ].needsLayout ) childNeedsLayoutThisRender = true;
				}
				else
				{
					// First, render non-containers.
					//
					elements [ i ].render ( );
				}
			}
			
			if ( containers.length == 0 ) 
			{
				pE ( "Container.renderChildren" );
				return;
			}

			if ( needRender.length > 0 )
			{
				if ( debugOptions [ "render" ] ) trace ( node.@id, "renderChildren",  needRender.length );
				
				// initializeRender will clear previously calculated layout
				// informations.
				//
				for ( i = 0; i < needRender.length; i++ ) needRender [ i ].initializeRender ( );
				
				// A child needs layout if it's moving or resizing.
				//
				if ( childNeedsLayoutThisRender ) 
				{
					// Calling calculateDimensionStyles ( ) with no argument 
					// will only calculate values for non-leftover styles like
					// "padding: 50%;" or "width: auto;". We do this because we
					// the leftover calculations depend on these values.
					//
					for ( i = 0; i < needRender.length; i++ ) needRender [ i ].calculateDimensionStyles ( );
						
					var toRender : Array = [ ];
					var hasContentSizedContainers : Boolean = false;
					
					for ( i = 0; i < needRender.length; i++ )
					{
						if ( ! isNaN ( needRender [ i ].width ) && ! isNaN ( needRender [ i ].height ) )
						{
							// Here we render those elements for which we know the width
							// and height. These are the easy elements and after this
							// step we're largely done with sizing them.
							//
							needRender [ i ].renderChildren ( );
							needRender [ i ].layoutChildren ( true );
						}
						else
						{
							// This next if statement is looking for elements that do
							// not have determinable widths and heights and that are not
							// to be calculated using leftover (thus the "is String"
							// shortcut check). If we find any then these are special
							// elements that are sized based on the elements they
							// contain.
							//
							// In any case, though, this element needs more work so we
							// push it on the toRender list.
							//
							if ( ( isNaN ( needRender [ i ].width ) && ! ( needRender [ i ].width is String ) ) || ( isNaN ( needRender [ i ].height ) && ! ( needRender [ i ].height is String ) ) ) hasContentSizedContainers = true;
							toRender.push ( needRender [ i ] );
						}
					}
	
					if ( hasContentSizedContainers )
					{
						// We have elements determining at least one of their 
						// dimensions based on their content so we need to render
						// their children. Now these elements may have some 
						// leftover-dependent dimensions so we try to evaluate
						// those as best we can now. To do that, we need to
						// layout this element's children elements and then call
						// calculateDimensionStyles ( true ) on each. (This call
						// to calculateDimensionStyles will focus solely on 
						// on dimensions styles using leftover.
						//
						layoutChildren ( true );
						for ( i = 0; i < needRender.length; i++ ) needRender [ i ].calculateDimensionStyles ( true );
						
						for ( i = 0; i < toRender.length; i++ )
						{
							if ( ( isNaN ( toRender [ i ].width ) && ! ( toRender [ i ].width is String ) ) || ( isNaN ( toRender [ i ].height ) && ! ( toRender [ i ].height is String ) ) )
							{
								// This element is determining at least one of its
								// dimensions based on its content so we need to
								// render its children to move us forward in our
								// calculations but we'll still have to come back to 
								// it later because it does have a leftover 
								// dimension.
								//
								toRender [ i ].renderChildren ( );
								toRender [ i ].layoutChildren ( true );
								
								// Because the toRender element is reporting a
								// width and height that might change before we
								// finish, we need to call checkForResize here.
								//
								toRender [ i ].checkForResize ( );
							}
						}
						
						// This next call basically erases the leftover dimension
						// style calculations be did at the beginning of this 
						// conditional statement. The idea is that now that the
						// elements whose dimensions are based on the size of their 
						// children are sized, we will lay them out with their 
						// siblings to get a new evaluation of all the leftover
						// styles. That step is of course necessary to size those
						// elements whose dimensions are completely determined by
						// leftover styles.
						//
						for ( i = 0; i < needRender.length; i++ ) needRender [ i ].calculateDimensionStyles ( );
					}
				
					// Our children with non-leftover dimensions and dimensions
					// determined by their content are sized now so we lay them
					// out in preparation for evaluating any leftover styles.
					//
					layoutChildren ( true );
					
					// Now calculate those leftover styles.
					//
					for ( i = 0; i < needRender.length; i++ ) needRender [ i ].calculateDimensionStyles ( true );
					
					// Run through the toRender list and render and layout their 
					// children now that their dimensions are calculated. Note
					// that this is repeated for those elements with a dimension
					// based on its contents. XXX Should it be?
					// 
					for ( i = 0; i < toRender.length; i++ )
					{
						toRender [ i ].renderChildren ( );
						toRender [ i ].layoutChildren ( true );
					}
					
					// Now all of our children elements should have their 
					// dimensions calculated so we can do a final layout.
					//
					layoutChildren ( true );
					
					// But if any element resized during the render process then
					// we may need to do another iteration at the nest render
					// cycle.
					//
					for ( i = 0; i < needRender.length; i++ ) needRender [ i ].checkForResize ( );
				}
				else
				{
					for ( i = 0; i < needRender.length; i++ ) 
					{
						needRender [ i ].renderChildren ( );
						needRender [ i ].layoutChildren ( true );
					}
				}
				
				for ( i = 0; i < needRender.length; i++ ) 
				{
					needRender [ i ].renderSelf ( );
					needRender [ i ].finalizeRender ( );
					needRender [ i ].positionChildren ( );
				}
			}
			else if ( childNeedsLayoutThisRender )
			{
				layoutChildren ( true );
			}
			
			positionChildren ( );
			
			if ( lastScrollbarWidth != scrollbarWidth || lastScrollbarHeight != scrollbarHeight ) 
			{
				if ( debugOptions [ "render" ] ) trace ( node.@id, "resizeOnScrollbarChange",  "(", lastScrollbarWidth, ",", lastScrollbarHeight, ") -> (", scrollbarWidth, ",", scrollbarHeight, ")" );
				needsLayout = true;
				dispatchEvent ( new FrontalEvent ( FrontalEvent.RESIZE ) );
			}
			
			lastScrollbarWidth = scrollbarWidth;
			lastScrollbarHeight = scrollbarHeight;
			
			pE ( "Container.renderChildren" );
		}
		
		internal function positionChildren ( ) : void
		{
			for ( var i : uint = 0; i < containers.length; i++ ) containers [ i ].positionMovie ( );
		}
		
		internal function checkForResize ( ) : void
		{
			if ( display == "none" ) return;
			
			// Saw a math rounding error when using leftover and a text tag thus
			// the 0.0001 threshhold.
			//
			
			var resizedThisCalculation : Boolean = false;
			var newWidth : Number = containerWidth / scaleX - borderWidth - paddingWidth - marginWidth;
			var newHeight : Number = containerHeight / scaleY - borderHeight - paddingHeight - marginHeight;
			if ( isNaN ( lastWidth  ) || Math.abs ( lastWidth  - newWidth  ) > 0.0001 ) resizedThisCalculation = true;
			if ( isNaN ( lastHeight ) || Math.abs ( lastHeight - newHeight ) > 0.0001 ) resizedThisCalculation = true;

			if ( resizedThisCalculation && debugOptions [ "render" ] ) trace ( node.@id, "resized",  "(", lastWidth, ",", lastHeight, ") -> (", newWidth, ",", newHeight, ")" );
				
			lastWidth = newWidth;
			lastHeight = newHeight;

			// Save our work so that the next call to get the explicit dimension
			// is correct.
			//
			if ( widthSpecified  ) __explicitContainerWidth  = lastWidth  - scrollbarWidth;
			if ( heightSpecified ) __explicitContainerHeight = lastHeight - scrollbarHeight;

			if ( resizedThisCalculation ) 
			{
				dispatchEvent ( new FrontalEvent ( FrontalEvent.RESIZE ) );
			}
		}
		
		/*
			Function: showChildren
	
				Show each of our children. Whether or not they are visible is
				determined by their styles.
				
				If we have a manager then we let it handle this process at least
				for manageable Containers.
		*/
		/**
		 * @private
		 */
		public function showChildren ( ) : void
		{
			for ( var i : uint = 0; i < containers.length; i++ ) containers [ i ].show ( );
			
			// XXX Is this okay with a mix of manageable an unmanageable
			// children?
			//
			resetDepths ( );
		}
		
		/*
			Function: layoutChildren
			
				The children containers have been rendered and now must be lain
				out. If our layout style is "stack" then all of the children
				containers get placed at the top, left corner of our content
				area. If the layout style is not set, then the children
				containers get placed according to the details below.
				
				In both cases, any scrollbar containers are placed outside of
				these layout rules and at the top, left, bottom or right of the
				content area depending on the "scrollbar-placement" style. The
				placement of the scrollbar may affect the layout of the children
				because it restricts the area in which they can flow and in the
				case of a top or left scrollbar also causes them to shift.
			
				The handling of floating containers in CSS is sometimes
				unintuitive and we have not tried very hard to simulate those
				more esoteric cases. What we have implemented should be useful
				in achieving what we feel are practical and likely layouts.
				
				When laying out a container, we look at its "clear" style. If it
				is "none" then we do nothing. If it is "left" then we update
				minFloatLeftY to be the lowest edge of all the left floating
				containers and we set the "leftCleared" flag to be true. Then we
				look at the "float" style of the container. If it is also "left"
				then no further steps are needs wrt processing the "clear"
				style. If it is "right" then we set the minFloatRightY to be the
				max of it and minFloatLeftY so that this right floating
				container is placed below any previous left floating containers.
				Similarly, if it is "none" then we set minNormalY to the maximum
				of it and minFloatLeftY.
				
				We do something similar if the "clear" style is "right" and if
				it's "both" then we do all of these steps.
				
				Then we need to actually position the container and to do so we
				look at the container's "float" style. If it is "left" then we
				need to look at the leftCleared flag. If it's true then we set
				the top threshhold for the left floats to be the max of the
				bottom of all the left floats and the current bottom of the
				normal containers. We also assert that the container will be
				flush left with the parent container. Then we place it as high
				as we can without intersecting any previously placed floating
				containers and respecting the threshhold and assertion mentioned
				previously.
				
				If the leftCleared flag is false then we place the left
				container as high and as left as we can while not extruding from
				the parent container, going above the minFloatLeftY threshhold
				or intersecting with any other floating elements. After
				placement, we set the minFloatLeftY to the top of this container
				and set the leftCleared flag to false.
				
				We follow a similar procedure for right floating containers. 
				
				Normal floating containers are placed without respect to the
				floating containers save that they are always placed below
				minNormalY which may be affected by floats via the "clear"
				style.
				
				As a layout convenience mechanism, we have the notion of a left
				margin within a container that is initially zero but is then set
				whenever we see a "left" style. For example, the two red
				rectangles should be vertically aligned even though the latter
				does not have a left style:
				
					<?xml version="1.0" encoding="UTF-8"?>
					<div>
						<div style="left: 400px; width: 100px; height: 100px; background-color: #ff0000;" />
						<div style="width: 100px; height: 100px; background-color: #ff0000;" />
					</div>
				
				The idea is to make it easier to reposition a bunch of
				containers without the heaviness of wrapping them in another
				container. This feature is not supported with the "right" style.
				
				Setting left, top, bottom and right on a floating container does
				not make much sense and so is generally ignored except for the
				situation mentioned above.
				
			Parameters:
			
				positive	- Is the direction of the scroll positive or 
							  negative?
							  
			Returns:
			
				The amount in pixels that the scrollbar should scroll under each
				event. This amount is always positive. (The base class will 
				negate the returned value if necessary.)
		*/
		private var candidate : Rectangle = new Rectangle ( );
		private var childrenPositionsSet : Boolean = false;
		private var floatLefts : Array = [ ];
		private var floatRights : Array = [ ];
		private var floatLeftCt : uint = 0;
		private var floatRightCt : uint = 0;
		private var minNormalY : Number = 0;

		/**
		 * @private
		 */
		public function layoutChildren ( refreshCache : Boolean = false ) : Array
		{
			if ( refreshCache ) clearLayoutCache ( );
			if ( childrenPositionsSet ) return childrenPositions;

			pB ( "Container.layoutChildren" );
			
			if ( debugOptions [ "render" ] ) trace ( node.@id, "layoutChildren", "explicitContainerWidth:", explicitContainerWidth );
				
			childrenPositions.length = containers.length;
			floatLefts.length = containers.length;
			floatRights.length = containers.length;
			floatLeftCt = 0;
			floatRightCt = 0;

			if ( getStyle ( "layout" ) == "stack" )
			{
				// For the stack layout, we ignore all float information and
				// position each child relative to ( 0, 0 ).
				//
				for ( var i : int = 0; i < containers.length; i++ ) 
				{
					childrenPositions [ i ] = containers [ i ].getFloatNormalPosition ( 0, 0 );
				}
			}
			else
			{
				var minFloatLeftY : Number = 0;
				var minFloatRightY : Number = 0;
				var floatLeftBottom : Number = 0;
				var floatRightBottom : Number = 0;
				var normalBottom : Number = 0;
				var leftCleared : Boolean = true;
				var rightCleared : Boolean = true;
				minNormalY = 0;
				
				for ( i = 0; i < containers.length; i++ )
				{
					var sibling : Container = containers [ i ];
					if ( sibling.containingBlock != this || sibling.isScrollbar ) 
					{
						childrenPositions [ i ] = sibling.getFloatNormalPosition ( );
						continue;
					}
					
					if ( sibling.display == "none" )
					{
						sibling.candidate.setEmpty ( );
						childrenPositions [ i ] = sibling.candidate;
						continue;
					}
					
					var clear : String = sibling.getStyle ( "clear" ) as String;
					var float : String = sibling.getStyle ( "float" ) as String;
					
					if ( clear == "both" || clear == "left" )
					{
						leftCleared = true;
						minFloatLeftY = floatLeftBottom;
						if ( float == "right" ) minFloatRightY = minFloatRightY > minFloatLeftY ? minFloatRightY : minFloatLeftY;
						if ( float == "none" ) minNormalY = minNormalY > minFloatLeftY ? minNormalY : minFloatLeftY;
					}
							
					if ( clear == "both" || clear == "right" )
					{
						rightCleared = true;
						minFloatRightY = Math.max ( minFloatRightY, floatRightBottom );
						if ( float == "left" ) minFloatLeftY = minFloatLeftY > minFloatRightY ? minFloatLeftY : minFloatRightY;
						if ( float == "none" ) minNormalY = minNormalY > minFloatRightY ? minNormalY : minFloatRightY;
					}
							
					if ( float == "left" )
					{
						var candidateX : Number = 0;
						if ( ! leftCleared && floatLeftCt > 0 ) candidateX = floatLefts [ floatLeftCt - 1 ].right;
						sibling.candidate.left = candidateX;
						sibling.candidate.top = minFloatLeftY > minNormalY ? minFloatLeftY : minNormalY;
						sibling.candidate.width = sibling.containerWidth;
						sibling.candidate.height = sibling.containerHeight;
						
						if ( sibling.candidate.right > explicitContainerWidth )
						{
							// We've exceeded our parent container so float left
							// as far as possible.
							//
							sibling.candidate.x = 0;
							sibling.candidate.y = sibling.candidate.y > floatLeftBottom ? sibling.candidate.y : floatLeftBottom;
						}
						
						for ( var j : int = 0; j < floatRightCt; j++ )
						{
							if ( floatsIntersect ( sibling.candidate, floatRights [ j ] ) )
							{
								// We've run into a right float so float left.
								//
								sibling.candidate.x = 0;
								sibling.candidate.y = sibling.candidate.y > floatLeftBottom ? sibling.candidate.y : floatLeftBottom;
							}
							
							// We're still hitting the right float so float
							// below it.
							//
							if ( floatsIntersect ( sibling.candidate, floatRights [ j ] ) ) sibling.candidate.y = sibling.candidate.y > floatRights [ j ].bottom ? sibling.candidate.y : floatRights [ j ].bottom;
						}
						
						floatLefts [ floatLeftCt++ ] = sibling.candidate;
						minFloatLeftY = sibling.candidate.top;
						floatLeftBottom = floatLeftBottom > sibling.candidate.bottom ? floatLeftBottom : sibling.candidate.bottom;
						leftCleared = false;
					}
					else if ( float == "right" )
					{
						candidateX = explicitContainerWidth - sibling.containerWidth;
						if ( ! rightCleared && floatRightCt > 0 ) candidateX = floatRights [ floatRightCt - 1 ].left - sibling.containerWidth;
						sibling.candidate.left = candidateX;
						sibling.candidate.top = minFloatRightY > minNormalY ? minFloatRightY : minNormalY;
						sibling.candidate.width = sibling.containerWidth;
						sibling.candidate.height = sibling.containerHeight;
						
						if ( sibling.candidate.left < 0 )
						{
							// We've exceeded our parent container so float right
							// as far as possible.
							//
							sibling.candidate.x = explicitContainerWidth - sibling.containerWidth;
							sibling.candidate.y = sibling.candidate.y > floatRightBottom ? sibling.candidate.y : floatRightBottom;
						}
						
						for ( j = 0; j < floatLeftCt; j++ )
						{
							if ( floatsIntersect ( sibling.candidate, floatLefts [ j ] ) )
							{
								// We've run into a left float so float right.
								//
								sibling.candidate.x = explicitContainerWidth - sibling.containerWidth;
								sibling.candidate.y = sibling.candidate.y > floatRightBottom ? sibling.candidate.y : floatRightBottom;
							}
							
							// We're still hitting the left float so float
							// below it.
							//
							if ( floatsIntersect ( sibling.candidate, floatLefts [ j ] ) ) sibling.candidate.y = sibling.candidate.y > floatLefts [ j ].bottom ? sibling.candidate.y : floatLefts [ j ].bottom;
						}
						
						floatRights [ floatRightCt++ ] = sibling.candidate;
						minFloatRightY = sibling.candidate.top;
						floatRightBottom = floatRightBottom > sibling.candidate.bottom ? floatRightBottom : sibling.candidate.bottom;
						rightCleared = false;
					}
					else
					{
						sibling.candidate = sibling.getFloatNormalPosition ( 0, minNormalY );
						minNormalY = sibling.candidate.bottom;
					}
					
					childrenPositions [ i ] = sibling.candidate;
				}
			}
			
			childrenPositionsSet = true;

			// Calculate our childrenDimensions which is the bounding box of
			// all of our children containers. We use this to determine our
			// scrollability.
			//
			childrenDimensions.setEmpty ( );
			for ( i = 0; i < childrenPositions.length; i++ )
			{
				if ( ! containers [ i ].isScrollbar && containers [ i ].containingBlock === this )
				{
					childrenDimensions = childrenDimensions.union ( childrenPositions [ i ] );
				}
			}
	
			// Layout our scrollbars.
			//
			if ( scrollbarVert  != null )
			{
				var offset : Point = new Point ( getStyle ( "scrollbar-placement" ).indexOf ( "right" ) >= 0 ? explicitContainerWidth : 0, getStyle ( "scrollbar-placement" ).indexOf ( "top"    ) >= 0 ? scrollbarHeight         : 0 );
				childrenPositions [ scrollbarVert.childIndex  ].offsetPoint ( offset );
				if ( scrollbarVert.debugOptions [ "render" ] ) trace ( scrollbarVert.node.@id, "offsetScrollbarVert", scrollbarVert.getStyle ( "scrollbar-placement" ), offset );
			}
			
			if ( scrollbarHoriz != null ) 
			{
				offset             = new Point ( getStyle ( "scrollbar-placement" ).indexOf ( "left"  ) >= 0 ? scrollbarWidth         : 0, getStyle ( "scrollbar-placement" ).indexOf ( "bottom" ) >= 0 ? explicitContainerHeight : 0 );
				childrenPositions [ scrollbarHoriz.childIndex  ].offsetPoint ( offset );
				if ( scrollbarHoriz.debugOptions [ "render" ] ) trace ( scrollbarHoriz.node.@id, "offsetScrollbarHoriz", scrollbarHoriz.getStyle ( "scrollbar-placement" ), offset );
			}
			
			// Reposition our children around the scrollbars.
			//
			for ( i = 0; i < childrenPositions.length; i++ )
			{
				if ( ! containers [ i ].isScrollbar && containers [ i ].containingBlock === this )
				{
					childrenPositions [ i ].offset ( getStyle ( "scrollbar-placement" ).indexOf ( "left"  ) >= 0 ? scrollbarWidth : 0, getStyle ( "scrollbar-placement" ).indexOf ( "top" ) >= 0 ? scrollbarHeight : 0 );
				}
			}
			
			pE ( "Container.layoutChildren" );
			
			return childrenPositions;
		}			
			
		private var childrenLeftovers : Array = [ ];
		
		/**
		 * @private
		 */
		internal function getChildLeftover ( childIndex : uint ) : Object
		{
			if ( ! childrenPositionsSet ) return null;
			if ( containers.length < childIndex ) return null;
			if ( childrenLeftovers [ childIndex ] != null && childrenLeftovers [ childIndex ].isSet ) return childrenLeftovers [ childIndex ];
			
			if ( debugOptions [ "render" ] ) trace ( node.@id, "calculateChildLeftovers" );
				
			childrenLeftovers.length = containers.length;
			if ( childrenLeftovers [ childIndex ] == null ) childrenLeftovers [ childIndex ] = { width: 0, height: 0, isSet: false };
			
			// Calculate the child's "leftover" or how far it can expand without 
			// causing a re-flow.
			//
		
			var candidate : Rectangle = new Rectangle ( );
				
			if ( containers [ childIndex ].isScrollbar || containers [ childIndex ].containingBlock != this )
			{
				// Calculate the leftover of a targetted Container or
				// Stage as if this child Container were all it
				// contained.
				//
				var targetWidth  : Number = containers [ childIndex ].containingBlock == null ? document.getPositiveStage ( ).width  : containers [ childIndex ].containingBlock.containerWidth;
				var targetHeight : Number = containers [ childIndex ].containingBlock == null ? document.getPositiveStage ( ).height : containers [ childIndex ].containingBlock.containerHeight;
				childrenLeftovers [ childIndex ].width  = containers [ childIndex ] == scrollbarVert  ? 0 : targetWidth  - childrenPositions [ childIndex ].right; 
				childrenLeftovers [ childIndex ].height = containers [ childIndex ] == scrollbarHoriz ? 0 : targetHeight - childrenPositions [ childIndex ].bottom;
				childrenLeftovers [ childIndex ].isSet = true;
			}
			else 
			{
				/*
					Calculate how much wider and taller this
					container can be without a) causing it and its
					siblings to exceed their parent's explicitContainerWidth
					and explicitContainerHeight and b) causing it's flow to
					change. Obviously calculating the latter depends
					on which layout is in effect.
					
					In all layouts, a container is expanded right
					and down in the calculations except in the 
					normal layout for right-floating containers in
					which case the containers are expanded left and
					down. 
				*/
				if ( getStyle ( "layout" ) == "stack" )
				{
					childrenLeftovers [ childIndex ].width  = explicitContainerWidth  - childrenPositions [ childIndex ].right; 
					childrenLeftovers [ childIndex ].height = explicitContainerHeight - childrenPositions [ childIndex ].bottom;
					childrenLeftovers [ childIndex ].isSet = true;
				}
				else
				{
					switch ( containers [ childIndex ].getStyle ( "float" ) )
					{                                     
						case "left":
							/*
							We base our right edge calculations off the
							right-most container in our row.
							
							Our right edge candidates are the
							explicitContainerWidth and the left edge of
							any right-floating containers. So we build a
							candidate list, sort it and check them out
							until we succeed.
							
							Our bottom edge candidate are the
							explicitContainerHeight and the top edge of
							any floating containers that is greater than
							the current bottom edge.
							*/                          
							var edgeCandidates : Array = [ explicitContainerWidth ];
							for ( var j : uint = 0; j < floatRightCt; j++ ) edgeCandidates.push ( floatRights [ j ].left );
							edgeCandidates.sort ( Array.NUMERIC );
							var candidateIndex : uint = childIndex;
							for ( j = childIndex + 1; j < childrenPositions.length; j++ ) 
							{
								if ( childrenPositions [ j ].top != childrenPositions [ childIndex ].top || containers [ j ].getStyle ( "float" ) != "left" ) break;
								candidateIndex = j;
							}
							candidate.left = childrenPositions [ candidateIndex ].left;
							candidate.top = childrenPositions [ candidateIndex ].top;
							candidate.bottom = childrenPositions [ candidateIndex ].bottom;
							var maxRightEdge : Number;
							for ( j = 0; j < edgeCandidates.length; j++ )
							{                           
								candidate.right = edgeCandidates [ j ];
								var intersects : Boolean = false;
								for ( var k : uint = 0; k < floatRightCt; k++ )
								{
									if ( floatsIntersect ( candidate, floatRights [ k ] ) )
									{
										intersects = true;
										break;
									}
								}
								if ( ! intersects ) maxRightEdge = edgeCandidates [ j ];
								if ( intersects ) break;
							}
							
							// This width result is the same for the ith
							// to the candidateIndex child so just 
							// duplicate it and update the loop counter.
							//
							edgeCandidates = [ explicitContainerHeight ];
							for ( j = 0; j < floatRightCt; j++ ) edgeCandidates.push ( floatRights [ j ].top );
							for ( j = 0; j < floatLeftCt;  j++ ) edgeCandidates.push ( floatLefts  [ j ].top );
							edgeCandidates.sort ( Array.NUMERIC );
							for ( k = childIndex; k <= candidateIndex; k++ )
							{
								// For each, calculate the height 
								// leftover.
								//
								candidate.left = childrenPositions [ k ].left;
								candidate.top = childrenPositions [ k ].top;
								candidate.right = childrenPositions [ k ].right;
								var maxBottomEdge : Number = childrenPositions [ k ].bottom;
								for ( j = 0; j < edgeCandidates.length; j++ )
								{                           
									if ( edgeCandidates [ j ] <= childrenPositions [ k ].bottom ) continue;
									
									candidate.bottom = edgeCandidates [ j ];
									intersects = false;
									for ( var l : uint = candidateIndex + 1; l < childrenPositions.length; l++ )
									{
										if ( containers [ l ].getStyle ( "float" ) != "none" && floatsIntersect ( candidate, childrenPositions [ l ] ) )
										{
											intersects = true;
											break;
										}
									}
									if ( ! intersects ) maxBottomEdge = edgeCandidates [ j ];
									if ( intersects ) break;
								}
							
								if ( childrenLeftovers [ k ] == null ) childrenLeftovers [ k ] = { width: 0, height: 0, isSet: false };
								childrenLeftovers [ k ].width  = isNaN ( maxRightEdge ) ? 0 : maxRightEdge - childrenPositions [ candidateIndex ].right; 
								childrenLeftovers [ k ].height = maxBottomEdge - childrenPositions [ k ].bottom; 
								childrenLeftovers [ k ].isSet = true;
							}
							break;
							
						case "right":
							// Our calculations for right floating
							// containers is similar to those done
							// above for left floating ones 
							//
							edgeCandidates = [ 0 ];
							for ( j = 0; j < floatLeftCt; j++ ) edgeCandidates.push ( floatLefts [ j ].right );
							edgeCandidates.sort ( Array.NUMERIC | Array.DESCENDING );
							candidateIndex = childIndex;
							for ( j = childIndex + 1; j < childrenPositions.length; j++ ) 
							{
								if ( childrenPositions [ j ].top != childrenPositions [ childIndex ].top || containers [ j ].getStyle ( "float" ) != "right" ) break;
								candidateIndex = j;
							}
							candidate.right = childrenPositions [ candidateIndex ].right;
							candidate.top = childrenPositions [ candidateIndex ].top;
							candidate.bottom = childrenPositions [ candidateIndex ].bottom;
							var minLeftEdge : Number;
							for ( j = 0; j < edgeCandidates.length; j++ )
							{
								candidate.left = edgeCandidates [ j ];
								intersects = false;
								for ( k = 0; k < floatLeftCt; k++ )
								{
									if ( floatsIntersect ( candidate, floatLefts [ k ] ) )
									{
										intersects = true;
										break;
									}
								}
								if ( ! intersects ) minLeftEdge = edgeCandidates [ j ];
								if ( intersects ) break;
							}
							
							// This width result is the same for the ith
							// to the candidateIndex child so just 
							// duplicate it and update the loop counter.
							//
							edgeCandidates = [ explicitContainerHeight ];
							for ( j = 0; j < floatRightCt; j++ ) edgeCandidates.push ( floatRights [ j ].top );
							for ( j = 0; j < floatLeftCt;  j++ ) edgeCandidates.push ( floatLefts  [ j ].top );
							edgeCandidates.sort ( Array.NUMERIC );
							for ( k = childIndex; k <= candidateIndex; k++ )
							{
								// For each, calculate the height 
								// leftover.
								//
								candidate.left = childrenPositions [ k ].left;
								candidate.top = childrenPositions [ k ].top;
								candidate.right = childrenPositions [ k ].right;
								maxBottomEdge = childrenPositions [ k ].bottom;
								for ( j = 0; j < edgeCandidates.length; j++ )
								{                           
									if ( edgeCandidates [ j ] <= childrenPositions [ k ].bottom ) continue; 
									candidate.bottom = edgeCandidates [ j ];
									intersects = false;
									for ( l = candidateIndex + 1; l < childrenPositions.length; l++ )
									{
										if ( containers [ l ].getStyle ( "float" ) != "none" && floatsIntersect ( candidate, childrenPositions [ l ] ) )
										{
											intersects = true;
											break;
										}
									}
									if ( ! intersects ) maxBottomEdge = edgeCandidates [ j ];
									if ( intersects ) break;
								}
							
								if ( childrenLeftovers [ k ] == null ) childrenLeftovers [ k ] = { width: 0, height: 0, isSet: false };
								childrenLeftovers [ k ].width  = isNaN ( minLeftEdge ) ? 0 : childrenPositions [ candidateIndex ].left - minLeftEdge; 
								childrenLeftovers [ k ].height = maxBottomEdge - childrenPositions [ k ].bottom; 
								childrenLeftovers [ k ].isSet = true;
							}
							break;
							
						case "normal":
						default:
							childrenLeftovers [ childIndex ].width  = explicitContainerWidth  - childrenPositions [ childIndex ].right; 
							childrenLeftovers [ childIndex ].height = explicitContainerHeight - minNormalY;
							childrenLeftovers [ childIndex ].isSet = true;
							break;
					}
				}
			}

			return childrenLeftovers [ childIndex ];
		}
		
		/*
			Function: clearLayoutCache
			
				layoutChildren caches its results. This method clears that
				cached information.
		*/
		/**
		 * @private
		 */
		public function clearLayoutCache ( ) : void
		{
			childrenPositionsSet = false;
			for ( var i : uint = 0; i < containers.length; i++ ) 
			{
				if ( childrenLeftovers [ i ] != null ) childrenLeftovers [ i ].isSet = false;
				containers [ i ].siblingsLayoutCalculated = false;
			}
		}
			
		// Return the placement of this container as if it were laid out
		// without any float style at the given position.
		//
		/**
		 * @private
		 */
		internal function getFloatNormalPosition ( x : Number = 0, y : Number = 0 ) : Rectangle
		{
			var position : Rectangle = new Rectangle ( x, y, containerWidth, containerHeight );
			
			if ( ! isNaN ( left ) )
			{
				position.x = left;
			}
			else if ( ! isNaN ( right ) ) 
			{
				position.x = right - containerWidth;
			}
			
			if ( ! isNaN ( top ) ) 
			{
				position.y = top;
			}
			else if ( ! isNaN ( bottom ) )
			{
				position.y = bottom - containerHeight;
			}
			
			if ( containingBlock != parentContainer )
			{
				var target : DisplayObject = containingBlock == null ? document.movieParent.stage : containingBlock.movie;
				
				if ( movie != null && movie.parent != null && target != null )
				{
					// We need to shift our coordinates to our containing block.
					//
					var offset : Point = target.localToGlobal ( position.topLeft );
					offset = movie.parent.globalToLocal ( offset ).subtract ( position.topLeft );
					position.offsetPoint ( offset );
				}

				// Our position is dependent on our parent Container and our
				// target Container so add listeners to them for reposition
				// events.
				//
				addListener ( parent, FrontalEvent.REPOSITION, pinnedContainerRepositionHandler );
				if ( containingBlock != null ) addListener ( containingBlock, FrontalEvent.REPOSITION, pinnedContainerRepositionHandler );
			}
			
			return position;
		}
		
		/**
		 * @private
		 */
		internal function pinnedContainerRepositionHandler ( event : FrontalEvent ) : void
		{
			repositionPinnedContainer ( );
		}
		
		/**
		 * @private
		 */
		internal function repositionPinnedContainer ( ) : void
		{
			parentContainer.layoutChildren ( );
			parentContainer.childrenPositions [ childIndex ] = getFloatNormalPosition ( );
			positionMovie ( );
		}
		
		
		/*
			Function: floatsIntersect
			
				This function behaves like the Flash's Rectangle.intersects 
				except that a zero width or height rectangle can still cause an 
				intersection. This is helpful for laying out floating elements
				for which we don't yet have a height.
		*/
		/**
		 * @private
		 */
		internal function floatsIntersect ( rect1 : Rectangle, rect2 : Rectangle ) : Boolean
		{
			var result : Boolean = true;

			if ( ! rect1.intersects ( rect2 ) )
			{
				var horizontalResult : Boolean = false;
				if  ( rect1.width == 0 && rect2.width != 0 )
				{
					horizontalResult = rect2.left < rect1.left && rect2.right > rect1.right;
				}
				else if  ( rect1.width != 0 && rect2.width == 0 )
				{
					horizontalResult = rect1.left < rect2.left && rect1.right > rect2.right;
				}
				else if ( rect1.top == rect2.top )
				{
					// Widths are set and tops are aligned.
					//
					horizontalResult = ! ( rect1.right <= rect2.left || rect1.left >= rect2.right );
				}
				
				var verticalResult : Boolean = false;
				if  ( rect1.height == 0 && rect2.height != 0 )
				{
					verticalResult = rect2.top < rect1.top && rect2.bottom > rect1.bottom;
				}
				else if  ( rect1.height != 0 && rect2.height == 0 )
				{
					verticalResult = rect1.top < rect2.top && rect1.bottom > rect2.bottom;
				}
				else if ( rect1.width == 0 && rect2.width == 0 && rect1.left == rect2.left )
				{
					// Heights are set and lefts are aligned.
					//
					verticalResult = ! ( rect1.bottom <= rect2.top || rect1.top >= rect2.bottom );
				}
				
				result = horizontalResult || verticalResult;
			}
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function get widthSpecified ( ) : Boolean
		{
			return ( gS ( "width" ) != null && ( gS ( "width" ) != "auto" || isNaN ( intrinsicWidth ) ) ) || ( gS ( "left" ) != null && gS ( "left" ) != "auto" && gS ( "right" ) != null && gS ( "right" ) != "auto" );
		}
		
		internal function get nonAutoWidthSpecified ( ) : Boolean
		{
			// This is similar to widthSpecified but does not allow for a width 
			// of "auto." This is useful for elements with intrinsic width that
			// want to know if the intrinsice dimensions are being used or not.
			// Note the we do not say widthSpecified && gS ( "width" ) != "auto"
			// because that will call intrinsicWidth which at least in the case
			// of ContainerText.intrinsicWidth leads to infinite loop.
			//
			return ( gS ( "width" ) != null && gS ( "width" ) != "auto" ) || ( gS ( "left" ) != null && gS ( "left" ) != "auto" && gS ( "right" ) != null && gS ( "right" ) != "auto" );
		}
		
		/**
		 * @private
		 */
		internal function get heightSpecified ( ) : Boolean
		{
			return ( gS ( "height" ) != null && ( gS ( "height" ) != "auto" || isNaN ( intrinsicHeight ) ) ) || ( gS ( "top" ) != null && gS ( "top" ) != "auto" && gS ( "bottom" ) != null && gS ( "bottom" ) != "auto" );
		}
		
		internal function get nonAutoHeightSpecified ( ) : Boolean
		{
			// See nonAutoWidthSpecified.
			//
			return ( gS ( "height" ) != null && gS ( "height" ) != "auto" ) || ( gS ( "top" ) != null && gS ( "top" ) != "auto" && gS ( "bottom" ) != null && gS ( "bottom" ) != "auto" );
		}
		
		/**
		 * @private
		 */
		internal function calculateDimensionStyles ( leftover : Boolean = false ) : void
		{
			if ( display == "none" ) 
			{
				scaleX = scaleY = 1;
				left = right = top = bottom = undefined;
				marginTop = marginBottom = marginLeft = marginRight = undefined;
				paddingTop = paddingBottom = paddingLeft = paddingRight = undefined;
				borderTop = borderBottom = borderLeft = borderRight = undefined;
				lastWidth = lastHeight = undefined;
				return;
			}
				
			pB ( "Container.calculateDimensionStyles" );
			
			// Cache a few styles for performance reasons. (These variables are 
			// used in the following calculations so leave this code here.)
			//
			if ( getStyle ( "scale" ) != null )
			{
				scaleX = scaleY = getStyle ( "scale" );
			}
			else
			{
				scaleX	= getStyle ( "scale-x" );
				scaleY	= getStyle ( "scale-y" );
			}
			
			left				= internalEvaluateDimensionStyle ( getStyle ( "left"					), true,  left,			    leftover );
			right				= internalEvaluateDimensionStyle ( getStyle ( "right"					), true,  right,			leftover );
			top					= internalEvaluateDimensionStyle ( getStyle ( "top"						), false, top,			    leftover );
			bottom				= internalEvaluateDimensionStyle ( getStyle ( "bottom"					), false, bottom,			leftover );
			marginTop			= internalEvaluateDimensionStyle ( getStyle ( "margin-top"				), false, marginTop,		leftover );
			marginBottom		= internalEvaluateDimensionStyle ( getStyle ( "margin-bottom"			), false, marginBottom,	    leftover );
			marginLeft			= internalEvaluateDimensionStyle ( getStyle ( "margin-left"				), true,  marginLeft,		leftover );
			marginRight			= internalEvaluateDimensionStyle ( getStyle ( "margin-right"			), true,  marginRight,	    leftover );
			paddingTop			= internalEvaluateDimensionStyle ( getStyle ( "padding-top"				), false, paddingTop,		leftover );
			paddingBottom		= internalEvaluateDimensionStyle ( getStyle ( "padding-bottom"			), false, paddingBottom,	leftover );
			paddingLeft			= internalEvaluateDimensionStyle ( getStyle ( "padding-left"			), true,  paddingLeft,	    leftover );
			paddingRight		= internalEvaluateDimensionStyle ( getStyle ( "padding-right"			), true,  paddingRight,	    leftover );
			borderTop			= internalEvaluateDimensionStyle ( getStyle ( "border-top-width"		), false, borderTop,		leftover );
			borderBottom		= internalEvaluateDimensionStyle ( getStyle ( "border-bottom-width"		), false, borderBottom,	    leftover );
			borderLeft			= internalEvaluateDimensionStyle ( getStyle ( "border-left-width"		), true,  borderLeft,		leftover );
			borderRight			= internalEvaluateDimensionStyle ( getStyle ( "border-right-width"		), true,  borderRight,	    leftover );
			
			// Local functions with String functions are faster than regular 
			// expressions it seems.
			//
			function hasToken ( value : *, token : String, leftover : Boolean ) : Boolean 
			{
				return value != null && isNaN ( value ) && value.toString ( ).indexOf ( token ) >= 0 && ( ( leftover && value.toString ( ).indexOf ( "leftover" ) >= 0 ) || ( ! leftover && value.toString ( ).indexOf ( "leftover" ) < 0 ) ); 
			}
			
			// For width and height, if their length is measured wrt leftover
			// then we need to be sure to add in the contentWidth and
			// contentHeight and scrollbar dimensions since they are part of 
			// these dimensions but are not in the leftover.
			//
			if ( leftover )
			{
				var widthBonus  : Number = hasToken ( getStyle ( "width"  ), "%", leftover ) ? contentWidth  + scrollbarWidth  : 0;
				var heightBonus : Number = hasToken ( getStyle ( "height" ), "%", leftover ) ? contentHeight + scrollbarHeight : 0;
			}
			
			width  = internalEvaluateDimensionStyle ( getStyle ( "width"  ), true,  width,  leftover );
			height = internalEvaluateDimensionStyle ( getStyle ( "height" ), false, height, leftover );
			
			if ( leftover )
			{
				width  += widthBonus;
				height += heightBonus;
			}
			
			// Handle "auto" values.
			//
			// If width or height are auto and we have an intrinsic width or
			// height, e.g., we're an image or a video, then we use that as our
			// dimension.
			//
			// XXX What does "auto leftover" mean here? We ignore it.
			//
			// Also, if no width is set then again we use the intrinsic width.
			//
			if ( ! leftover )
			{
				if ( 
						( 
							! nonAutoWidthSpecified || 
							hasToken ( width,  "auto", leftover ) 
						) && ! isNaN ( intrinsicWidth ) 
					)
				{
					width  = intrinsicWidth;
				}
				
				if ( 
						( 
							! nonAutoHeightSpecified || 
							hasToken ( height,  "auto", leftover ) 
						) && ! isNaN ( intrinsicHeight ) 
					)
				{
					height  = intrinsicHeight;
				}
			}
			
			// In this discussion, we focus on our width styles. A similar
			// discussion pertains to our height styles.
			//
			// If we do not have a float style or if our parent has the "stack"
			// layout, then we have these rules for styles with a value of 
			// "auto":
			//
			// left  -> 0
			// right -> 100%
			//
			// If there is one "auto" value among marginLeft, borderLeft,
			// paddingLeft, width, paddingRight, borderRight and marginRight 
			// then we deduce its value from the fact that the sum of those
			// values must be equal to our target width. Our target width is
			// the value of the right style or 100% if not specified minus the 
			// value of our left style or 0 if not specified.
			//
			// If more that one value is "auto" including width, then all but
			// the width are set to 0 and the width is deduced from the 
			// calculation above.
			//
			// If marginLeft and marginRight are auto then we use the above
			// equation to calculate their sum and split is evenly between the
			// two.
			//
			
			var widthProperties  : Array = [ "marginLeft", "marginRight", "borderLeft", "borderRight", "paddingLeft", "paddingRight" ];
			var heightProperties : Array = [ "marginTop", "marginBottom", "borderTop", "borderBottom", "paddingTop", "paddingBottom" ];
			
			for each ( var dimension : String in [ "width", "height" ] )
			{
				var properties : Array = dimension == "width" ? widthProperties : heightProperties;
					
				if ( dimension == "width" )
				{
					if ( hasToken ( left,  "auto", leftover ) ) left   = 0;
					if ( hasToken ( right, "auto", leftover ) ) right  = internalEvaluateDimensionStyle ( "100%", true, right, leftover );
				}
				else
				{
					if ( hasToken ( top,    "auto", leftover ) ) top    = 0;
					if ( hasToken ( bottom, "auto", leftover ) ) bottom = internalEvaluateDimensionStyle ( "100%", false, bottom, leftover );
				}
				
				if ( ( parent != null && parent.getStyle ( "layout" ) == "stack" ) || getStyle ( "float" ) == "none" )
				{
					var total : Number = 0;
					var autoCount : Number = 0;
					
					for each ( var property : String in properties ) 
					{
						if (  hasToken ( this [ property ], "auto", leftover ) ) 
						{
							autoCount++;
						}
						else if ( ! isNaN ( this [ property ] ) ) 
						{
							total += this [ property ];
						}
					}
					
					if ( ! isNaN ( this [ dimension ] ) ) 
					{
						total += this [ dimension ];
					}
					
					if ( hasToken ( this [ dimension ], "auto", leftover ) )
					{
						this [ dimension ] = ( dimension == "width" ? getTargetWidth ( leftover ) : getTargetHeight ( leftover ) ) - total;
						for each ( property in properties ) 
						{
							if ( hasToken ( this [ property ], "auto", leftover ) ) this [ property ] = 0;
						}
					}
					else
					{
						if ( ! leftover && isNaN ( this [ dimension ] ) && childrenPositionsSet && ! isNaN ( contentDimensions ( ) [ dimension ] ) )
						{
							// Our width or height isn't defined by a style but we
							// have laid out our children so we know what it is. Add
							// it to our total so our "auto" calculations are more
							// accurate.
							//
							total += contentDimensions ( ) [ dimension ];
						}
						
						if ( autoCount == 1 )
						{
							for each ( property in properties ) 
							{
								this [ property ] = hasToken ( this [ property ], "auto", leftover ) ? ( dimension == "width" ? getTargetWidth ( leftover ) : getTargetHeight ( leftover ) ) - total : 0;
							}
						}
						else if ( dimension == "width" && autoCount == 2 && hasToken ( marginLeft, "auto", leftover ) && hasToken ( marginRight, "auto", leftover ) )
						{
							// For "auto leftover" margins, just divide the 
							// leftover by two. This is in contrast to "auto"
							// margins which are 100% of the parent's dimension 
							// minus the fixed elements divide by two.
							//
							marginLeft = marginRight = leftover ? ( getTargetWidth ( leftover ) / 2 ) : ( ( getTargetWidth ( ) - total ) / 2 );
						}
						else if ( dimension == "height" && autoCount == 2 && hasToken ( marginTop, "auto", leftover ) && hasToken ( marginBottom, "auto", leftover ) )
						{
							marginTop = marginBottom = leftover ? ( getTargetHeight ( leftover ) / 2 ) : ( ( getTargetHeight ( ) - total ) / 2 );
						}
						else if ( autoCount > 1 )
						{
							if ( hasToken ( this [ property ], "auto", leftover ) ) this [ property ] = 0;
							Debugger.logMessage ( Debugger.WARNING, "Container", "more auto values requested than can be determined for " + Util.htmlEscape ( toXMLString ( ).substr ( 0, 100 ) ) );
						}
					}
				}
				else
				{
					if ( isNaN ( this [ dimension ] ) && hasToken ( this [ dimension ], "auto", leftover ) ) this [ dimension ] = Number.NaN;
					for each ( property in properties ) 
					{
						if ( hasToken ( this [ property ], "auto", leftover ) ) this [ property ] = 0;
					}
				}
			}
			
			// Handle the case that we've set a left and right or top and bottom
			// and no width or height. (In which case we want our width and 
			// height to stretch to fill out this space.)
			//
			if ( isNaN ( width  ) && ! isNaN ( left ) && ! isNaN ( right  ) ) width  = right - left - marginWidth - borderWidth - paddingWidth;
			if ( isNaN ( height ) && ! isNaN ( top  ) && ! isNaN ( bottom ) ) height = bottom - top - marginHeight - borderHeight - paddingHeight;
			
			if ( debugOptions [ "render" ] ) 
			{
				trace ( node.@id, "calculateDimensionStyles", "marginLeft:", marginLeft, "borderLeft:", borderLeft, "paddingLeft:", paddingLeft, "width:", width, "paddingRight:", paddingRight, "borderRight:", borderRight, "marginRight:", marginRight );
				trace ( node.@id, "calculateDimensionStyles", "marginTop:", marginTop, "borderTop:", borderTop, "paddingTop:", paddingTop, "height:", height, "paddingBottom:", paddingBottom, "borderBottom:", borderBottom, "marginBottom:", marginBottom );
			}
				
			// Our width and height are now calculated so we can cache our
			// explicitContainerWidth and explicitContainerHeight. This is so
			// when our children calculate their dimensions they are against the
			// correct parent's dimensions.
			//
			// So, we cache the dimensions if our styles say they are specified
			// and we have calculated them or if we don't have a style 
			// specified. This latter one is a bit oddball. It allows for a
			// construct like this:
			//
			// <div><div style="width: 100%; height: 100%; background-color: red;" /></div>
			//
			// This will fill the stage with red even though it doesn't 
			// necessarily make sense to.
			//
			if ( ( widthSpecified  && ! isNaN ( width  ) ) || gS ( "width"  ) == null ) cacheExplicitContainerWidth  ( true );
			if ( ( heightSpecified && ! isNaN ( height ) ) || gS ( "height" ) == null ) cacheExplicitContainerHeight ( true );
			
			pE ( "Container.calculateDimensionStyles" );
		}
		
		/*
			Function: getTargetWidth
			
				When calculating values for style with an "auto" value, we use
				this calculation as our target width. We do not inline this
				because it may potentially touch explicitContainerWidth which we
				only want to do if we really need to as it triggers adding a
				resize-event listener.
		*/
		/**
		 * @private
		 */
		internal function getTargetWidth ( leftover : Boolean = false ) : Number
		{
			var leftEdge  : Number = isNaN ( left  ) ? 0 : left;
			var rightEdge : Number = isNaN ( right ) ? internalEvaluateDimensionStyle ( "100%" + ( leftover ? " leftover" : "" ), true, rightEdge, leftover ) : right;
			return rightEdge - leftEdge;
		}
		
		/*
			Function: getTargetHeight
			
				When calculating values for style with an "auto" value, we use
				this calculation as our target height. We do not inline this
				because it may potentially touch explicitContainerHeight which we
				only want to do if we really need to as it triggers adding a
				resize-event listener.
		*/
		/**
		 * @private
		 */
		internal function getTargetHeight ( leftover : Boolean = false ) : Number
		{
			var topEdge    : Number = isNaN ( top    ) ? 0 : top;
			var bottomEdge : Number = isNaN ( bottom ) ? internalEvaluateDimensionStyle ( "100%" + ( leftover ? " leftover" : "" ), false, bottomEdge, leftover ) : bottom;
			return bottomEdge - topEdge;
		}
		
		/**
		 * @private
		 */
		public function evaluateDimensionStyle ( styleValue : *, isWidth : Boolean, currentValue : * = NaN ) : *
		{
			var value : * = internalEvaluateDimensionStyle ( styleValue, isWidth, currentValue, false );
			value = internalEvaluateDimensionStyle ( styleValue, isWidth, value, true );
			return value;
		}
		
		/**
		 * @private
		 */
		internal function internalEvaluateDimensionStyle ( value : *, isWidth : Boolean, currentValue : * = NaN, leftover : Boolean = false ) : *
		{
			if ( leftover && ! isNaN ( currentValue ) ) return currentValue;
			if ( ! isNaN ( value ) ) return Number ( value );
			
			var percentageIndex : int = -1;
			var leftoverIndex : int = -1;
			if ( value != null && typeof ( value ) == "string" )
			{
				percentageIndex = value.indexOf ( "%" );
				leftoverIndex = value.indexOf ( "leftover" );
			}

			if ( percentageIndex > 0 && ( ( leftover && leftoverIndex > percentageIndex ) || ( ! leftover && leftoverIndex < 0 ) ) )
			{
				var result : Number = Number ( value.substr ( 0, percentageIndex ) );
				var explicitDimension : Number;
				
				needsLayoutOnResize = true;
				if ( leftover ) usingLeftover = true;
					
				if ( getStyle ( "pin-to-stage" ) )
				{
					explicitDimension = document.getPositiveStage ( ) [ isWidth ? "width" : "height" ];
					if ( leftover ) 
					{
						if ( isWidth )
						{
							explicitDimension -= marginWidth + borderWidth + paddingWidth + scrollbarWidth;
						}
						else
						{
							explicitDimension -= marginHeight + borderHeight + paddingHeight + scrollbarHeight;
						}
					}
					addStageResizeListener ( );
					
					if ( debugOptions [ "render" ] ) trace ( node.@id, "internalEvaluateDimensionStyle (resize to stage)", "value:", value, "isWidth:", isWidth, "explicitDimension:", explicitDimension * result / 100 );
				}
				else
				{
					var container : Container = containingBlock;
					
					if ( container != null )
					{
						// We always touch our parent's explicitContainer* variables to
						// potentially trigger the needed resize listener.
						//
						// Note that we're using this dimension. This way we can
						// be more judicious about reacting to resize events.
						//
						if ( isWidth )
						{
							explicitDimension = container.explicitContainerWidth;
							usingWidthDimensions [ container ] = explicitDimension;
						}
						else
						{
							explicitDimension = container.explicitContainerHeight;
							usingHeightDimensions [ container ] = explicitDimension;
						}
						
						// XXX This appears broken if containingBlock is not the
						// same as parentContainer.
						//
						if ( leftover && parentContainer.getChildLeftover ( childIndex ) != null )
						{
							if ( debugOptions [ "render" ] ) trace ( node.@id, "internalEvaluateDimensionStyle using child leftover" );
							explicitDimension = parentContainer.getChildLeftover ( childIndex ) [ isWidth ? "width" : "height" ];
						}
						
						if ( debugOptions [ "render" ] ) trace ( node.@id, "internalEvaluateDimensionStyle", "value:", value, "isWidth:", isWidth, "explicitDimension:", explicitDimension * result / 100 );

						addResizeListener ( container );
					}
					else if ( this === document )
					{
						// Handle the special case of the document element.
						//
						explicitDimension = isWidth ? containingBlockExplicitWidth : containingBlockExplicitHeight;
						if ( leftover )
						{
							if ( isWidth ) 
							{
								explicitDimension -= paddingWidth + borderWidth + marginWidth + scrollbarWidth;
							}
							else
							{
								explicitDimension -= paddingHeight + borderHeight + marginHeight + scrollbarHeight;
							}
						}
						
						if ( debugOptions [ "render" ] ) trace ( node.@id, "internalEvaluateDimensionStyle (document)", "value:", value, "isWidth:", isWidth, "explicitDimension:", explicitDimension * result / 100 );
						
						addStageResizeListener ( );
					}
				}
				
				return explicitDimension * result / 100;
			}

			return leftover ? currentValue : value;
		}
		
		/**
		 * @private
		 */
		override public function applyStyles ( ) : void
		{
			super.applyStyles ( );
			
			if ( ! isNaN ( getAttribute ( "tabIndex" ) ) ) movie.tabIndex = Number ( getAttribute ( "tabIndex" ) );
			
			if ( movie.stage != null )
			{
				applyMovieStyles ( );
			}
			else
			{
				addListener ( movie, Event.ADDED_TO_STAGE, applyMovieStylesHandler );
			}
		}

		/**
		 * @private
		 */
		public function applyMovieStyles ( ) : void
		{
			if ( ! attributes.styles.styleCategoryHasChanges ( "movieFormat" ) ) return;
			attributes.styles.clearChanges ( "movieFormat" );

			// Set movie properties based on styles. These particular styles may
			// be absolute values or percentages.
			//
			// If you're here trying to figure out why tweening the scale of an
			// element with styles like this doesn't seem to be working:
			//
			// style="scale: .5; movie-registration-x: 50%; movie-registration-y: 50%; margin: auto;"
			//
			// then it's a bigger issue than you think. The problem is, does the
			// scale affect the margin or not? Currently it does and so the 
			// absolute width and height of the element or changing as the scale
			// changes. The result is that it doesn't appear to be scaling 
			// around it's center. If scale were not applied to margins then you
			// would get undesirable results on an element with fixed-width 
			// margins. In the current implementation, we can get the result we 
			// want by setting the margin to 0 and wrapping the element in 
			// another element with margin auto.
			//
			for each ( var entry : Array in [ [ "movie-registration-x", "registrationX" ], [ "movie-registration-y", "registrationY" ], [ "depth", "z" ] ] )
			{
				var value : * = getStyle ( entry [ 0 ] );
				if ( value != null && typeof ( value ) == "string" )
				{
					var baseValue : Number;
					switch ( entry [ 0 ] )
					{
						case "movie-registration-x":
							baseValue = contentWidth;
							break;
						case "movie-registration-y":
							baseValue = contentHeight;
							break;
						case "depth":
							if ( parentContainer != null ) baseValue = parentContainer.movie.z;
							if ( isNaN ( baseValue ) ) baseValue = 0; 
							break;
					}
						
					var percentageIndex : int = value.indexOf ( "%" );
					if ( percentageIndex > 0 ) value = Number ( value.substr ( 0, percentageIndex ) ) * baseValue / 100;
				}
				if ( ! isNaN ( value ) ) movie [ entry [ 1 ] ] = value;
			}

			for each ( var style : String in StylesManager.moviePropertyStyles )
			{
				value = getStyle ( style );
				
				if ( value !== undefined )
				{
					var styleDefn : Object = document.stylesManager.getStyleDefn ( style );
					if ( styleDefn.options.maskedByStyle == null || getStyle ( styleDefn.options.maskedByStyle ) === undefined )
					{
						movie [ styleDefn.options.movieProperty ] = value;
					}
				}
			}
		}
		
		/**
		 * @private
		 */
		internal function applyMovieStylesHandler ( event : Event ) : void
		{
			removeListener ( movie, Event.ADDED_TO_STAGE, applyMovieStylesHandler );
			applyMovieStyles ( );
		}
		
		/**
		 * @private
		 */
		override public function set isLink ( value : Boolean ) : void
		{
			super.isLink = value;
		
			// Listen for a click or a double click to mark us as visited.
			//
			if ( value && ! isVisited && ! getStyle ( "is-marker" ) )
			{
				addListener ( movie, MouseEvent.CLICK,		  markAsVisitedHandler );
				addListener ( movie, MouseEvent.DOUBLE_CLICK, markAsVisitedHandler );
			}
			else
			{
				removeListener ( movie, MouseEvent.CLICK,		 markAsVisitedHandler );
				removeListener ( movie, MouseEvent.DOUBLE_CLICK, markAsVisitedHandler );
			}
		}
		
		/**
		 * @private
		 */
		public function markAsVisitedHandler ( event : MouseEvent ) : void
		{
			removeListener ( movie, MouseEvent.CLICK,		 markAsVisitedHandler );
			removeListener ( movie, MouseEvent.DOUBLE_CLICK, markAsVisitedHandler );
			isVisited = true;
		}
		
		/*
			Function: getMappedStyleValue
			
				In cases where the style maps to a movie, then we can get a 
				current value for it.
				
			Parameters:
			
				style	- The style of interest.
				
			Returns:
			
				The mapped value of the style.
		*/
		/**
		 * @private
		 */
		override public function getMappedStyleValue ( style : String ) : *
		{
			if ( movie != null )
			{
				var styleDefn : Object = document.stylesManager.getStyleDefn ( style );
				if ( styleDefn.options.movieProperty != null ) return movie [ styleDefn.options.movieProperty ];
			}
			
			return super.getMappedStyleValue ( style );
		}
		
		/*
			Function: synchStyles
			
				This function does the opposite of applyStyles. Whereas that
				internal function applied a series of styles to the movie (like setting
				the movie's alpha based on the "opacity" style), this function
				will attempt to take the current state of the movie and set the
				styles accordingly.
				
				Synching the position is a little more intrusive and so it may
				be turned off. This is more intrusive because it will overwrite 
				any value for left and top and will clear the float style.
				
			Parameters:
			
				synchPosition	- If true then synch the left and top styles 
								  with the movie's position. This will 
								  necessarily clear any float style.
				
			Notes:
			
				XXX Should this be integrated with getMappedStyleValue()? It
				seems like it. And synchStyles should work with video an text as
				well.
		*/
		/**
		 * Takes the current state of the movie and set its	styles accordingly.
		 */
		public function synchStyles ( synchPosition : Boolean = true ) : void
		{
			for each ( var style : String in StylesManager.moviePropertyStyles )
			{
				var styleDefn : Object = document.stylesManager.getStyleDefn ( style );
				var property : String = styleDefn.options.movieProperty;
				
				// XXX Certain properties (z, rotationX, rotationY, rotationZ 
				// and scaleZ) will turn on 3D rendering if set. So for these, 
				// if the style is not set and theor value is zero then we 
				// ignore it.
				//
				if ( 
					attributes.styles.getStyle ( style ) == null &&
					(
						property == "z" ||
						property == "rotationX" ||
						property == "rotationY" ||
						property == "rotationZ" ||
						property == "scaleZ"
					) &&
					(
						( property == "scaleZ" && movie [ property ] == 1 ) ||
						( property != "scaleZ" && movie [ property ] == 0 )
					) )
					continue;
					
				attributes.styles.setStyle ( style, movie [ property ], false, true );
			}
			
			if ( synchPosition )
			{
				left = ( movie.x - movieOffsetX ) / scaleX + ( parentContainer != null && gS ( "scrollable" ) !== false ? parentContainer.scrollLeft : 0 );
				top  = ( movie.y - movieOffsetY ) / scaleY + ( parentContainer != null && gS ( "scrollable" ) !== false ? parentContainer.scrollTop  : 0 );
				attributes.styles.setStyle ( "left",  left,      false, true, true );
				attributes.styles.setStyle ( "top",   top,       false, true, true );
				attributes.styles.setStyle ( "float", undefined, false, true, true );
			}
		}
	
		/**
		 * @private
		 */
		override public function applyInteractions ( ) : void
		{
			super.applyInteractions ( );
			
			if ( attributesHaveChanges )
			{
				applyInteraction ( Event.TAB_CHILDREN_CHANGE,		movie );
				applyInteraction ( Event.TAB_ENABLED_CHANGE,		movie );
				applyInteraction ( Event.TAB_INDEX_CHANGE,			movie );
				applyInteraction ( FrontalEvent.ENTER_FRAME,		movie );
				applyInteraction ( Event.ADDED_TO_STAGE,			movie );
				applyInteraction ( Event.REMOVED_FROM_STAGE,		movie );
				applyInteraction ( FrontalEvent.REPOSITION );
			}
			
			applyInteractiveObjectInteractions ( );
		}
		
		/**
		 * @private
		 */
		internal function addedToStageHandler ( event : Event = null ) : void { applyInteractiveObjectInteractions ( true ); }
		
		/**
		 * @private
		 */
		internal function removedFromStageHandler ( event : Event = null ) : void { removeInteractiveObjectInteractions ( ); }
		
		/**
		 * @private
		 */
		public static var interactiveEvents : Array = [ 
				MouseEvent.CLICK, MouseEvent.DOUBLE_CLICK, FocusEvent.FOCUS_IN, 
				FocusEvent.FOCUS_OUT, KeyboardEvent.KEY_DOWN, FocusEvent.KEY_FOCUS_CHANGE, 
				KeyboardEvent.KEY_UP, MouseEvent.MOUSE_DOWN, FocusEvent.MOUSE_FOCUS_CHANGE, 
				MouseEvent.MOUSE_MOVE, MouseEvent.MOUSE_OUT, MouseEvent.MOUSE_OVER, 
				MouseEvent.MOUSE_UP, MouseEvent.MOUSE_WHEEL, MouseEvent.ROLL_OUT, 
				MouseEvent.ROLL_OVER
			];
			
		/**
		 * @private
		 */
		public function applyInteractiveObjectInteractions ( force : Boolean = false ) : void
		{
			var forceApplyButtonStyles : Boolean = false;
			
			if ( force || attributesHaveChanges )
			{
				addListener ( movie, Event.ADDED_TO_STAGE, addedToStageHandler );
				addListener ( movie, Event.REMOVED_FROM_STAGE, removedFromStageHandler );
				
				if ( movie.stage != null ) 
				{
					forceApplyButtonStyles = true;

					// Unset the button characteristics.
					//
					movie.buttonMode = false;
					movie.mouseChildren = true;
					movie.tabEnabled = false;
					
					// Handle our standard movie clip events. For these, we look for an
					// attribute like "on" + the captialized event string, e.g., onClick
					// or onRollOut.
					//
					for each ( var type : String in interactiveEvents )
					{
						var attribute : String = "on" + type.charAt ( 0 ).toUpperCase ( ) + type.substr ( 1 );
						
						if ( applyInteraction ( type, movie, attribute ) )
						{
							// Treat this movie as a button.
							//
							movie.buttonMode = true;
							movie.mouseChildren = false;
							movie.tabEnabled = true;
						}
						
						// Also try to add the raw interaction. The difference 
						// here is that setting this interaction does not set
						// mouseChildren to false and so mouse events "seep" 
						// through the display object.
						//
						applyInteraction ( type, movie, attribute + "Raw" );
					}
					
					// Simulate the AS2 onReleaseOutside event by adding a listener for
					// MOUSE_UP events on the stage after any MOUSE_DOWN event on the
					// movie.
					//
					addListener ( movie, MouseEvent.MOUSE_DOWN, applyMouseUpHandler );
				}
			}
			
			if ( movie.stage != null && ( forceApplyButtonStyles || attributes.styles.styleCategoryHasChanges ( "buttonFormat" ) ) )
			{
				attributes.styles.clearChanges ( "buttonFormat" );
				
				// Let our styles override our default button behavior.
				//
				if ( getStyle ( "button-mode"    ) !== undefined ) movie.buttonMode    = getStyle ( "button-mode" );
				if ( getStyle ( "mouse-children" ) !== undefined ) movie.mouseChildren = getStyle ( "mouse-children" );
				if ( getStyle ( "tab-enabled"    ) !== undefined ) movie.tabEnabled    = getStyle ( "tab-enabled" );
			}
		}
		
		private var _isMacFirefox : * = undefined;
		
		/**
		 * @private
		 */
		internal function applyMouseUpHandler ( event : MouseEvent ) : void
		{
			addListener ( movie.stage, MouseEvent.MOUSE_UP, onReleaseOutsideHandler );
			addListener ( movie.stage, MouseEvent.MOUSE_UP, onReleaseOutsideHandler, true );
			
			// XXX Massive hack for Firefox on the Mac. In that case, 
			// MOUSE_UP and MOUSE_LEAVE are broken. MOUSE_LEAVE is broken such
			// that it is not fired unless the mouse button is not pressed or
			// it is pressed and then released outside the stage. We can take
			// advantage of that to say the mouse was released outside.
			//
			if ( _isMacFirefox === undefined )
			{
				try
				{
					var userAgent : String = ExternalInterface.call ( "navigator.userAgent.toString" );
					var platform : String = ExternalInterface.call ( "navigator.platform.toString" );
					_isMacFirefox = userAgent != null && userAgent.indexOf ( "Firefox" ) >= 0 && platform != null && platform.indexOf ( "Mac" ) >= 0;
				}
				catch ( error : * )
				{
					_isMacFirefox = false;
				}
			}
			
			if ( _isMacFirefox ) addListener ( movie.stage, Event.MOUSE_LEAVE, onReleaseOutsideHandler );
		}
		
		/**
		 * @private
		 */
		internal function onReleaseOutsideHandler ( event : Event ) : void
		{
			removeListener ( movie.stage, MouseEvent.MOUSE_UP, onReleaseOutsideHandler );
			removeListener ( movie.stage, MouseEvent.MOUSE_UP, onReleaseOutsideHandler, true );
			removeListener ( movie.stage, Event.MOUSE_LEAVE, onReleaseOutsideHandler );
			runInteractions ( "onMouseUpAnywhere", { event : event } );
			if ( event.type == Event.MOUSE_LEAVE || ! movie.hitTestPoint ( ( event as MouseEvent ).stageX, ( event as MouseEvent ).stageY ) ) runInteractions ( "onMouseUpOutside", { event : event } );
		}
		
		/**
		 * @private
		 */
		public function removeInteractiveObjectInteractions ( ) : void
		{
			for each ( var i : String in interactiveEvents ) removeInteraction ( i, movie );
			removeInteraction ( MouseEvent.MOUSE_UP, movie.stage );
		}
		
		/*
			Function: resolveFont
			
				Whenever the container uses a font, it calls resolveFont. This
				will pass the request through the FontManager. If the font
				cannot be immediately resolved, the onFontResolved is registered
				with the FontManager and called when the font is resolved. That
				callback will cause the container to be re-rendered.
				
			Parameters:
			
				name	- Font name to resolve.
				
			Returns:
			
				An Object with keys name and isEmbedded.
				
			See also:
			
				<FontManager.resolveFont>
		*/
		/**
		 * @private
		 */
		public function resolveFont ( name : String ) : Object { return document.fontManager.resolveFont ( name, onFontResolved ); }
		public function onFontResolved ( fontName : String, resolvedFont : Font ) : void { needsRender = true; }
		
		/**
		 * @private
		 */
		override public function set enabled ( value : Boolean ) : void 
		{ 
			if ( movie != null && value != super.enabled )
			{
				// AS3 does not automatically disable events so we need to do so
				// manually. We just place a high-priority listener on the movie
				// clip that cancels certain mouse events.
				//
				for each ( var type : String in interactiveEvents ) 
				{
					// Ignore the events that drive our pseudo classes. This 
					// lets us handle the common case that we are hovering over
					// a marker when it becomes disabled and so would have 
					// blocked the ROLL_OUT event that would turn off the hover.
					// The result would be that the next time the marker were 
					// enabled it would be in the hover state. Similar examples
					// exist for the "active" and "focus" pseudoclasses.
					//
					if ( type == MouseEvent.ROLL_OUT || type == MouseEvent.ROLL_OVER || type == MouseEvent.MOUSE_DOWN || type == MouseEvent.MOUSE_UP || type == FocusEvent.FOCUS_IN || type == FocusEvent.FOCUS_OUT || type == FocusEvent.MOUSE_FOCUS_CHANGE || type == FocusEvent.KEY_FOCUS_CHANGE ) continue; 
					
					if ( ! value )
					{
						addListener ( movie, type, Util.eventBlockerHandler, false, 1000 );
					}
					else
					{
						removeListener ( movie, type, Util.eventBlockerHandler, false );
					}
				}
			}
			
			super.enabled = value;
		}
		
		/*
			Function: renderProgressIndicator
			
				Render a progress indicator for this Container. 
		*/	
		/**
		 * @private
		 */
		public var progressIndicator : Container;
		
		/**
		 * @private
		 */
		public function renderProgressIndicator ( ) : void
		{   
			if ( getStyle ( "show-progress-indicator" ) == "never" )
			{
				if ( progressIndicator != null ) progressIndicator.setStyle ( "display", "none" );
				return;
			}
			
			// Create (if needed) and show the progress indicator container.
			//
			if ( progressIndicator == null )
			{
				var progressIndicatorNode : XMLList = document.getTemplateByName ( getStyle ( "progress-indicator-template" ) ).copy ( );
				// XXX Assert there is a single node.
				document.insertXML ( progressIndicatorNode, this );
				progressIndicator = document.getNodeElement ( XML ( progressIndicatorNode ) ) as Container;
				progressIndicator.isManageable = false;
				needsRender = true;
				
				// This will get things started by adding ourself as a listener
				// to all of our children with loadable content.
				//
				getLoadProgress ( );
				
				// We also need to listen for new elements being added in case 
				// one is a child of ours and it has loadable content.
				//
				addListener ( document, FrontalEvent.ADD_ELEMENT, checkForLoadableContentHandler );
			}
		}
		
		/**
		 * @private
		 */
		internal function checkForLoadableContentHandler ( event : FrontalEvent ) : void { getLoadProgress ( ); }
		
		/**
		 * @private
		 */
		override internal function progressHandler ( event : FrontalEvent ) : void
		{
			super.progressHandler ( event );
			
			if ( getStyle ( "show-progress-indicator" ) != "never" )
			{
				progressIndicator.runInteractions ( "onProgress", { progress: getLoadProgress ( false ) } );
				needsRender = true;
			}
		}
		
		/*
			Function: renderScrollbars
			
				We are scrollable if our contentWidth is less than our
				unboundedContentWidth. If this is the case and our "scroll"
				style is not "none," then we create our scrollbar containers.
				
				Scrollbar containers are created as children of the container
				with the scrollable content. Their layout though is unrelated to
				the other children as they are always placed between the
				scrollable container's padding and border. As such, adding
				scrollbars causes the area for the content to be reduced and
				re-flowed. Vertical scrollbars may be placed at the left or
				right and horizontal scrollbars at the top or bottom of the
				scrollable container via the "scrollbar-placement" style which
				by default is "right bottom."
				
				If the "scroll-container-id" style is set, though, the situation
				changes. In that case, the scrollbar containers are added to the
				target container like a normal container. They then flow with
				other elements like normal. The "scrollbar-placement" style is
				ignored.
				
				The scrollbar containers are taken from the template whose id is
				given by the style "scrollbar-horizontal-template" or
				"scrollbar-vertical-template" as appropriate which are both by
				default "frScrollbar" - a template included in the default
				stylesheet. The structure of the template is such that there are
				potential child containers with the names up, down, left, right,
				track, trackButton and slider. If these containers are found
				then they are updated as expected as the scrollbar's state
				changes.
				
				After the scrollbar containers are created, its width or height
				style (depending on if it is horizontal or vertical) is set
				based on the styles "scrollbar-width" and "scrollbar-height."
				
				XXX We dispatch resize events from this method. It's a little
				weird to dispatch these here since we normally dispatch them in
				calculateDimensionStyles based on a change in the width or
				height. But scrollbars do not change the width and height - just
				the contentWidth and contentHeight. A quick test of changing the
				check in calculateDimensionStyles to be based on contentWidth
				and contentHeight result in a lot of extra renders but there
				surely is a general solution.
		*/
		/**
		 * @private
		 */
		public function get scrollbarWidth	( ) : Number 
		{ 
			var result : Number = 0;
			
			if ( scrollbarVert != null && scrollbarVert.containingBlock == scrollbarVert.parentContainer && scrollbarVert.getStyle  ( "display" ) == "block" )
			{
				if ( isNaN ( scrollbarVert.containerWidth ) ) 
				{
					// We're in the middle of rendering and laying out the
					// scrollbar so we still need to do a layout once that is
					// done.
					//
					needsLayout = true;
				}
				else
				{
					result = scrollbarVert.containerWidth;
				}
			}
			
			return result; 
		}

		/**
		 * @private
		 */
		public function get scrollbarHeight	( ) : Number 
		{ 
			var result : Number = 0;
			
			if ( scrollbarHoriz != null && scrollbarHoriz.containingBlock == scrollbarHoriz.parentContainer && scrollbarHoriz.getStyle  ( "display" ) == "block" )
			{
				if ( isNaN ( scrollbarHoriz.containerHeight ) ) 
				{
					// We're in the middle of rendering and laying out the
					// scrollbar so we still need to do a layout once that is
					// done.
					//
					needsLayout = true;
				}
				else
				{
					result = scrollbarHoriz.containerHeight;
				}
			}
			
			return result; 
		}
		
		/**
		 * @private
		 */
		public function renderScrollbars ( ) : void
		{   
			// Make sure we're not a part of a scrollbar already.
			//
			if ( addingScrollbar || getScrollbarContainer ( ) != null ) return;

			if ( getStyle ( "scroll" ) == "none" )
			{
				if ( scrollbarVert  != null ) scrollbarVert.setStyle  ( "display", "none" );
				if ( scrollbarHoriz != null ) scrollbarHoriz.setStyle ( "display", "none" );
				scrollTop = scrollTop;
				scrollLeft = scrollLeft;
				return;
			}
						
			for each ( var orientation : String in  [ "Vert", "Horiz" ] )
			{
				// XXX Note we only look at the content in the positive space to
				// determine if there should be a scrollbar. Any reason why?
				//
				if ( orientation == "Vert" )
				{
					var newScrollable : Boolean = unboundedContentHeight - ( contentHeight + scrollbarHeight ) > 0.5;
					
					if ( debugOptions [ "render" ] ) trace ( node.@id, "verticalScrollbarCheck", unboundedContentHeight, ">", contentHeight, "+", scrollbarHeight, newScrollable );

					var alwaysShow : RegExp = /^(vertical|both)$/;
					var property : String = "scrollbarVert";
					var templateStyle : String = "scrollbar-vertical-template";
					var dimension : String = "width";
					var dimensionStyle : String = "scrollbar-width";
					var scrollProperty : String = "scrollTop";
				}
				else
				{
					newScrollable = unboundedContentWidth - ( contentWidth + scrollbarWidth ) > 0.5;
					
					if ( debugOptions [ "render" ] ) trace ( node.@id, "horizontalScrollbarCheck", unboundedContentWidth, ">", contentWidth, "+", scrollbarWidth, newScrollable );

					alwaysShow = /^(horizontal|both)$/;
					property = "scrollbarHoriz";
					templateStyle = "scrollbar-horizontal-template";
					dimension = "height";
					dimensionStyle = "scrollbar-height";
					scrollProperty = "scrollLeft";
				}
			
				if ( newScrollable || alwaysShow.test ( getStyle ( "scroll" ) ) )
				{
					// Create (if needed) and show the scrollbar container.
					//
					if ( this [ property ] == null )
					{
						var scrollbarNode : XMLList = document.getTemplateByName ( getStyle ( templateStyle ) ).copy ( );
						// XXX Assert there is a single node.
						scrollbarNode.@__frontalIsScrollbar = "true";
						addingScrollbar = true;
						document.insertXML ( scrollbarNode, this );
						addingScrollbar = false;
						this [ property ] = document.getNodeElement ( XML ( scrollbarNode ) ) as Container;
						this [ property ].isScrollbar = true;
						this [ property ].isManageable = false;
						needsLayout = true;
					}

					this [ property ].setStyle ( dimension, getStyle ( dimensionStyle ) );
					this [ property ].setStyle ( "display", "block" );
					
					if ( getStyle ( "scrollbar-scale-slider" ) ) 
					{
						var slider : Container = this [ property ].getContainerByIndex ( "slider", false, false );
						if ( slider != null )
						{
							var track : Container = this [ property ].getContainerByIndex ( "track", false, false );
							if ( orientation == "Vert" )
							{
								var sliderLength : Number = Math.ceil ( track.height * ( contentHeight / unboundedContentHeight ) );
								slider.setStyle ( "height", sliderLength - ( slider.marginHeight + slider.borderHeight + slider.paddingHeight ) );
							}
							else
							{
								sliderLength = Math.ceil ( track.width * ( contentWidth / unboundedContentWidth ) );
								slider.setStyle ( "width", sliderLength - ( slider.marginWidth + slider.borderWidth + slider.paddingWidth ) );
							}
						}
					}
				}
				else
				{
					// Hide the scrollbar container.
					//
					if ( this [ property ] != null ) this [ property ].setStyle ( "display", "none" );
				}
				
				// Offset our children based on the current scroll position. This
				// will also sanitize the previous values.
				//
				this [ scrollProperty ] = this [ scrollProperty ];
			}
			
			// Enable mouse wheel scrolling if a scrollbar is visible.
			//
			if ( 
					getStyle ( "mouse-wheel-enabled" ) && 
					( ( scrollbarVert  != null && scrollbarVert.getStyle  ( "display" ) == "block" ) || 
					  ( scrollbarHoriz != null && scrollbarHoriz.getStyle ( "display" ) == "block" ) ) 
				)
			{
				if ( ! hasEventListener ( MouseEvent.MOUSE_WHEEL ) ) 
				{
					addListener ( movie, MouseEvent.MOUSE_WHEEL, mouseWheelScrollHandler );
				}
			}
			else
			{
				removeListener ( movie, MouseEvent.MOUSE_WHEEL, mouseWheelScrollHandler );
			}
		}
		
		/*
			Function: getScrollbarContainer
				
				This function is to be called in the context of a scrollbar
				container or its sub-containers. The method will scan up the
				DocumentElement tree until it finds a container that is the
				scrollbar container of its parent. It then returns that
				container.
				
			Returns:
			
				The next scrollbar container at or above this container in the
				DocumentElement tree.
		*/
		/**
		 * @private
		 */
		public function getScrollbarContainer ( ) : Container
		{
			if ( scrollbarContainer == null ) 
			{
				var scan : Container = this;
				
				while ( scan != document && scan != null )
				{
					if ( scan == scan.parentContainer.scrollbarVert || scan == scan.parentContainer.scrollbarHoriz ) break;
					scan = scan.parentContainer;
				}
				
				scrollbarContainer = scan;
			}
			
			return scrollbarContainer == document ? null : scrollbarContainer;
		}
		
		/**
		 * Gets the maximum value for scrollTop.
		 */
		public function get scrollTopMax ( ) : Number { return Math.max ( 0, unboundedContentHeight - contentHeight ); }
		
		public function set scrollTop ( value : Number ) : void
		{
			value = Math.max ( 0, Math.min ( scrollTopMax, value ) );
			
			var runInteraction : Boolean = value != _scrollTop;
			
			// XXX I'd like to only reposition the children when scrollTop
			// changes. But after renderChildren is called, the children have 
			// been reset to effectively have a scrollTop of 0 without 
			// actually setting scrollTop to 0. This is to preserve the current 
			// scrollTop so that in renderSelf it can be correctly restored.
			//
			_scrollTop = value;

			for ( var i : uint = 0; i < containers.length; i++ )
			{
				var sibling : Container = containers [ i ];
				if ( sibling.containingBlock == this && ! sibling.isScrollbar && ! sibling.getStyle ( "disable-rendering" ) && ! sibling.getStyle ( "disable-positioning" ) && sibling.gS ( "scrollable" ) !== false )
				{
					// XXX This doesn't dispatch the REPOSITION event like 
					// positionMovie does. That's why we manually run the
					// onReposition interaction. But is this a bug or an 
					// optimization?
					//
					var lastY : Number = sibling.movie.y;
					sibling.movie.y = sibling.yPos + sibling.movieOffsetY - _scrollTop;
					if ( lastY != sibling.movie.y ) sibling.runInteractions ( "onReposition" );
				}
			}
			
			if ( scrollbarVert != null )
			{
				if ( scrollbarVert != scrollbarDragging )
				{
					// We're dragging so don't update the position.
					//
					var slider : Container = scrollbarVert.getContainerByIndex ( "slider", false, false );
					if ( slider != null )
					{
						var newY : Number = _scrollTop / scrollTopMax * ( ( slider.parent as Container ).height - slider.containerHeight ) + slider.movieOffsetY;
						if ( slider.movie.y != newY )
						{
							slider.movie.y = newY;
							slider.synchStyles ( );
						}
					}
				}
				
				if ( getStyle ( "scroll-disable-buttons" ) )
				{
					var up : Container = scrollbarVert.getContainerByIndex ( "up", false, false );
					if ( up != null ) up.enabled = value != 0;
					var down : Container = scrollbarVert.getContainerByIndex ( "down", false, false );
					if ( down != null ) down.enabled = value != scrollTopMax;
				}
			}
			
			if ( runInteraction ) runInteractions ( "onScroll", { isVertical: true } );
		}

		public function get scrollTop ( ) : Number { return _scrollTop; }
		
		/**
		 * Gets the maximum value for scrollLeft.
		 */
		public function get scrollLeftMax ( ) : Number { return Math.max ( 0, unboundedContentWidth - contentWidth ); }
		
		public function set scrollLeft ( value : Number ) : void
		{
			value = Math.max ( 0, Math.min ( scrollLeftMax, value ) );

			var runInteraction : Boolean = value != _scrollLeft;
			
			// XXX I'd like to only reposition the children when scrollLeft
			// changes. But after renderChildren is called, the children have 
			// been reset to effectively have a scrollLeft of 0 without 
			// actually setting scrollLeft to 0. This is to preserve the current 
			// scrollLeft so that in renderSelf it can be correctly restored.
			//
			_scrollLeft = value;

			for ( var i : uint = 0; i < containers.length; i++ )
			{
				var sibling : Container = containers [ i ];
				if ( sibling.containingBlock == this && ! sibling.isScrollbar && ! sibling.getStyle ( "disable-rendering" ) && ! sibling.getStyle ( "disable-positioning" ) && sibling.gS ( "scrollable" ) !== false ) 
				{
					// XXX This doesn't dispatch the REPOSITION event like 
					// positionMovie does. That's why we manually run the
					// onReposition interaction. But is this a bug or an 
					// optimization?
					//
					var lastX : Number = sibling.movie.x;
					sibling.movie.x = sibling.xPos + sibling.movieOffsetX - _scrollLeft;
					if ( lastX != sibling.movie.x ) sibling.runInteractions ( "onReposition" );
				}
			}
			
			if ( scrollbarHoriz != null )
			{
				if ( scrollbarHoriz != scrollbarDragging )
				{
					// We're dragging so don't update the position.
					//
					var slider : Container = scrollbarHoriz.getContainerByIndex ( "slider", false, false );
					if ( slider != null )
					{
						var newX : Number = _scrollLeft / scrollLeftMax * ( ( slider.parent as Container ).width - slider.containerWidth ) + slider.movieOffsetX;
						if ( slider.movie.x != newX )
						{
							slider.movie.x = newX;
							slider.synchStyles ( );
						}
					}
				}
				
				if ( getStyle ( "scroll-disable-buttons" ) )
				{
					var left : Container = scrollbarHoriz.getContainerByIndex ( "left", false, false );
					if ( left != null ) left.enabled = value != 0;
					var right : Container = scrollbarHoriz.getContainerByIndex ( "right", false, false );
					if ( right != null ) right.enabled = value != scrollTopMax;
				}
			}
			
			if ( runInteraction ) runInteractions ( "onScroll", { isVertical: false } );
		}

		public function get scrollLeft ( ) : Number { return _scrollLeft; }
		
		/*
			Function: scrollBy
			
				Scroll the containers content by delta amount either up and down
				or left and right. If the current scroll value plus delta is out
				of range then this will scroll to the extreme value. In that 
				case, it will also shorten then amount of time spent getting to
				that extreme by a ratio of the distance to it over delta.
				
			Parameters:
			
				delta		- How much to scroll by in pixels. delta is a length 
							  so the following are valid: 100, "100px," "80%."
							  Percentages are relative to the container's
							  dimensions and not the content's.
				positive	- Whether to scroll down or right (true) or up or
							  down (false). Default is true.
				isVertical	- Whether to scroll vertically (true) or 
							  horizontally (false).
				immediately	- Whether to tween to the new position (false) or 
							  to jump there immediately (true).
		*/
		/**
		 * Scrolls the contents of the element.
		 */
		public function scrollBy ( delta : *, positive : Boolean = true, isVertical : Boolean = true, immediately : Boolean = false ) : void
		{
			if ( containers.length == 0 ) return;
			delta = containers [ 0 ].internalEvaluateDimensionStyle ( delta, false );
			if ( isNaN ( delta ) ) return;
			if ( ! positive ) delta = -delta;
	
			var tween : Tween;
			var property : String;
			if ( isVertical )
			{
				if ( scrollTopTween == null ) scrollTopTween = new Tween ( this, "scrollTop", null, scrollTop, 0, 0, false );
				tween = scrollTopTween;
				property = "scrollTop";
			}
			else
			{
				if ( scrollLeftTween == null ) scrollLeftTween = new Tween ( this, "scrollLeft", null, scrollLeft, 0, 0, false );
				tween = scrollLeftTween;
				property = "scrollLeft";
			}
			
			var finish : Number = Math.max ( 0, Math.min ( this [ property + "Max" ], this [ property ] + delta ) );
			delta = finish - this [ property ];
			if ( delta == 0 ) return;
			
			tween.stop ( );
			var duration : Number = getScrollByDuration ( delta );
			if ( ! immediately && duration != 0 )
			{
				tween.begin = this [ property ];
				tween.finish = finish;
				tween.func = getStyle ( "scroll-tween-ease" );
				tween.useSeconds = getStyle ( "scroll-tween-use-secs" );
				if ( isNaN ( duration ) ) duration = 0;
				if ( getStyle ( "scroll-scale-tween-duration" ) ) duration = duration * ( tween.finish - tween.begin ) / delta;
				tween.duration = duration;
				if ( ! tween.useSeconds ) tween.duration = Math.floor ( tween.duration + 0.5 );
				if ( tween.duration == 0 )
				{
					this [ property ] += delta;
				}
				else
				{
					tween.start ( );
				}
			}
			else
			{
				this [ property ] += delta;
			}
		}
		
		/*
			Function: getScrollByDuration
			
				This simply returns the value of the "scroll-tween-duration"
				style. It is overridden by the ContainerText class to return 0
				(and thus prevent any multi-frame tweening) whenever scrollBy
				is being asked to scroll a single line. (In which case there is
				no animation possible as Flash scrolls TextFields by lines and
				not pixels.)
				
			Parameters:
			
				delta	- The requested amount to scroll. (The actual amount may
						  be less if delta extends the scroll past its valid
						  range.)
				
			Returns:
				
				The duration for the scrollBy tween. Return 0 if no tweening
				should be done.
		*/
		/**
		 * @private
		 */
		public function getScrollByDuration ( delta : Number ) : Number { return getStyle ( "scroll-tween-duration" ); }
		
		/**
		 * @private
		 */
		public function startDragScrollbarSlider ( ) : void
		{
			parentContainer.scrollbarDragging = this;
			var slider : Container = getContainerByIndex ( "slider", false, false );
			if ( this == parentContainer.scrollbarVert )
			{
				slider.movie.startDrag ( false, new Rectangle ( slider.movieOffsetX, slider.movieOffsetY, 0, Math.ceil ( slider.parentContainer.height - slider.containerHeight ) ) );
			}
			else
			{
				slider.movie.startDrag ( false, new Rectangle ( slider.movieOffsetX, slider.movieOffsetY, Math.ceil ( slider.parentContainer.width - slider.containerWidth ), 0 ) );
			}
			addListener ( movie.stage, MouseEvent.MOUSE_MOVE, dragScrollbarSliderHandler );
		}
		
		/**
		 * @private
		 */
		internal function dragScrollbarSliderHandler ( event : Event ) : void { synchToScrollbar ( ); }
		
		/**
		 * @private
		 */
		public function synchToScrollbar ( ) : void
		{
			var slider : Container = getContainerByIndex ( "slider", false, false );
			if ( this == parentContainer.scrollbarVert )
			{
				var scroll : Number = ( slider.movie.y - slider.movieOffsetY ) / ( ( slider.parent as Container ).height - slider.containerHeight );
				if ( ! isNaN ( scroll ) ) parentContainer.scrollTop = parentContainer.scrollTopMax * scroll;
			}
			else
			{
				scroll = ( slider.movie.x - slider.movieOffsetX ) / ( ( slider.parent as Container ).width - slider.containerWidth );
				if ( ! isNaN ( scroll ) ) parentContainer.scrollLeft = parentContainer.scrollLeftMax * scroll;
			}
			
			// XXX Too expensive!
			slider.synchStyles ( );
		}
		
		/**
		 * @private
		 */
		public function stopDragScrollbarSlider ( ) : void
		{
			parentContainer.scrollbarDragging = null;
			var slider : Container = getContainerByIndex ( "slider", false, false );
			slider.movie.stopDrag ( );
			removeListener ( movie.stage, MouseEvent.MOUSE_MOVE, dragScrollbarSliderHandler );
			synchToScrollbar ( );
		}
		
		/**
		 * @private
		 */
		public function startPressScrollButton ( positive : Boolean = true, isVertical : Boolean = true ) : void
		{
			scrollbarsState [ "scrollButton" + ( isVertical ? "Vert" : "Horiz" ) ] = { positive: positive, pressed: true, count: 0 };
			addListener ( movie, Event.ENTER_FRAME, isVertical ? scrollButtonVertPressedHandler : scrollButtonHorizPressedHandler );
		}
		
		/**
		 * @private
		 */
		public function scrollButtonVertPressedHandler ( event : Event ) : void
		{
			var entry : Object = scrollbarsState [ "scrollButtonVert" ];
			if ( entry == null || ! entry.pressed ) return;
			entry.count++;
			if ( entry.count > getStyle ( "click-max-frames" ) ) parentContainer.scrollBy ( parentContainer.getScrollByButtonPress ( entry.positive, true ), entry.positive, true, true );
		}
		
		/**
		 * @private
		 */
		public function scrollButtonHorizPressedHandler ( event : Event ) : void
		{
			var entry : Object = scrollbarsState [ "scrollButtonHoriz" ];
			if ( entry == null || ! entry.pressed ) return;
			entry.count++;
			if ( entry.count > getStyle ( "click-max-frames" ) ) parentContainer.scrollBy ( parentContainer.getScrollByButtonPress ( entry.positive, false ), entry.positive, false, true );
		}
		
		/**
		 * @private
		 */
		public function stopPressScrollButton ( isVertical : Boolean = true ) : void
		{
			var entry : Object = scrollbarsState [ "scrollButton" + ( isVertical ? "Vert" : "Horiz" ) ];
			if ( entry == null || ! entry.pressed ) return;
			entry.pressed = false;
			removeListener ( movie, Event.ENTER_FRAME, isVertical ? scrollButtonVertPressedHandler : scrollButtonHorizPressedHandler );
			if ( entry.count <= getStyle ( "click-max-frames" ) ) getScrollbarContainer ( ).parentContainer.scrollBy ( parentContainer.getScrollByButtonClick ( entry.positive, isVertical ), entry.positive, isVertical );
		}
		
		/*
			Functions: getScrollByButtonPress, getScrollByButtonClick
			
				These functions simply return the value for the styles
				"scroll-by-button-press" and "scroll-by-button-click"
				respectively. The parameter "positive" is ignored. The reason
				for these functions is to allow ContainerText to be able to
				easily override them which does so in order to support scrolling
				Flash TextFields. (Complications arise in that case because
				TextFields scroll vertically in lines and not pixels.)
				
			Parameters:
			
				positive	- Is the direction of the scroll positive or 
							  negative? This parameter is ignored in the base
							  implementation.
				isVertical	- Is this for a vertical scroll? This parameter is 
							  ignored in the base implementation.
							  
			Returns:
			
				The amount in pixels that the scrollbar should scroll under each
				event. This amount should always be positive regardless of the
				parameter "positive." This parameter is only provided to aid
				ContainerText in its calculations.
		*/
		/**
		 * @private
		 */
		public function getScrollByButtonPress ( positive : Boolean, isVertical : Boolean = true ) : * { return getStyle ( "scroll-by-button-press" ); }

		/**
		 * @private
		 */
		public function getScrollByButtonClick ( positive : Boolean, isVertical : Boolean = true ) : * { return getStyle ( "scroll-by-button-click" ); }
		
		/**
		 * @private
		 */
		public function startPressScrollbarTrack ( event : MouseEvent, isVertical : Boolean = true ) : void
		{
			var trackButton : Container = getContainerByIndex ( "trackButton", false, false );
			var entry : Object = { pressed: true, count: 0 };
			
			// If the click was below the slider then it's a positive scroll
			// otherwise it's negative.
			//
			var slider : Container = getContainerByIndex ( "slider", false, false );
			if ( isVertical )
			{
				entry.positive = slider != null ? event.localY >= slider.movie.y : event.localY >= trackButton.movieOffsetY + trackButton.height / 2;
			}
			else
			{
				entry.positive = slider != null ? event.localX >= slider.movie.x : event.localX >= trackButton.movieOffsetX + trackButton.width / 2;
			}

			scrollbarsState [ "scrollTrack" + ( isVertical ? "Vert" : "Horiz" ) ] = entry;
			addListener ( movie, Event.ENTER_FRAME, isVertical ? scrollbarTrackVertPressedHandler : scrollbarTrackHorizPressedHandler );
		}
		
		/**
		 * @private
		 */
		public function scrollbarTrackVertPressedHandler ( event : Event ) : void
		{
			var entry : Object = scrollbarsState [ "scrollTrackVert" ];
			if ( entry == null || ! entry.pressed ) return;
			entry.count++;
			if ( entry.count > getStyle ( "click-max-frames" ) ) parentContainer.scrollBy ( getStyle ( "scroll-by-track-press" ), entry.positive, true, true );
		}
		
		/**
		 * @private
		 */
		public function scrollbarTrackHorizPressedHandler ( event : Event ) : void
		{
			var entry : Object = scrollbarsState [ "scrollTrackHoriz" ];
			if ( entry == null || ! entry.pressed ) return;
			entry.count++;
			if ( entry.count > getStyle ( "click-max-frames" ) ) parentContainer.scrollBy ( getStyle ( "scroll-by-track-press" ), entry.positive, false, true );
		}
		
		/**
		 * @private
		 */
		public function stopPressScrollbarTrack ( isVertical : Boolean = true ) : void
		{
			var entry : Object = scrollbarsState [ "scrollTrack" + ( isVertical ? "Vert" : "Horiz" ) ];
			if ( entry == null || ! entry.pressed ) return;
			entry.pressed = false;
			removeListener ( movie, Event.ENTER_FRAME, isVertical ? scrollbarTrackVertPressedHandler : scrollbarTrackHorizPressedHandler );
			if ( entry.count <= getStyle ( "click-max-frames" ) ) getScrollbarContainer ( ).parentContainer.scrollBy ( getStyle ( "scroll-by-track-click" ), entry.positive, isVertical );
		}
		
		/**
		 * @private
		 */
		public function mouseWheelScrollHandler ( event : MouseEvent ) : void
		{
			scrollBy ( 
				getScrollByButtonClick ( event.delta < 0, true ) * getStyle ( "scroll-wheel-sensitivity" ),
				event.delta < 0,
				scrollbarVert != null && scrollbarVert.getStyle ( "display" ) == "block",
				! getStyle ( "scroll-wheel-smooth" )
			);
			event.stopPropagation();
		}		
    }
}

